PostgreSQL学习之限制用户单次登录时长

        PostgreSQL14已经实现了空闲会话超时断开功能,如果要控制用户登录时长(即不管会话是否空闲,只要超时就断开),时长可设置,并且可以精确到用户,测试验证如下:

        1、配置hba文件:hba文件增加一列,用于设置指定用户可以登录的时间长度(请忽略其中的TIME-RANGE一列),如

# TYPE  DATABASE        USER            ADDRESS        SESSION-TIME    TIME-RANGE         METHOD

# "local" is for Unix domain socket connections only
local   all             all                                     trust
# IPv4 local connections:
host    all             all             127.0.0.1/32  1  "MonTueWedThuFri|08:00:00-20:00:00,SatSun|10:00:00-16:00:00"     trust
# IPv6 local connections:
host    all             all             ::1/128   120    "MonTueWedThuFri|08:00:00-20:00:00,SatSun|10:00:00-16:00:00"          trust
# Allow replication connections from localhost, by a user with the
# replication privilege.
local   replication     all                                     trust
host    replication     all             127.0.0.1/32  120    "MonTueWedThuFri|08:00:00-20:00:00,SatSun|10:00:00-16:00:00"       trust
host    replication     all             ::1/128     120      "MonTueWedThuFri|08:00:00-20:00:00,SatSun|10:00:00-16:00:00"      trust

        2、修改hba.c文件:函数parse_hba_line逐行解析pg_hba.conf文件,在此函数里增加对会话时长一列进行解析并保存:

        field = lnext(tok_line->fields, field);
    	if (!field)
    	{
    		ereport(elevel,
    				(errcode(ERRCODE_CONFIG_FILE_ERROR),
    				 errmsg("end-of-line before session time"),
    				 errcontext("line %d of configuration file \"%s\"",
    							line_num, HbaFileName)));
    		*err_msg = "end-of-line before session method";
    		return NULL;
    	}
        tokens = lfirst(field);
    	if (tokens->length > 1)
    	{
    		ereport(elevel,
    				(errcode(ERRCODE_CONFIG_FILE_ERROR),
    				 errmsg("multiple values specified for session time"),
    				 errhint("Specify exactly one session time per line."),
    				 errcontext("line %d of configuration file \"%s\"",
    							line_num, HbaFileName)));
    		*err_msg = "multiple values specified for session time";
    		return NULL;
    	}
    	token = linitial(tokens);
        {
            char *endptr = NULL;
            parsedline->session_time = strtoint(token->string, &endptr, 10);
            if (endptr == token->string || errno == ERANGE || parsedline->session_time < 0){
        		ereport(elevel,
        				(errcode(ERRCODE_CONFIG_FILE_ERROR),
        				 errmsg("The time setting of the session time is incorrect"),
        				 errhint("Specify exactly one session time per line."),
        				 errcontext("line %d of configuration file \"%s\"",
        							line_num, HbaFileName)));
        		*err_msg = "The time setting of the session time is incorrect";
        		return NULL;
        	}
        }

        3、增加GUC参数:在postgresql.conf文件里增加参数session_timeout用于指定通用会话时长,默认为0,0为不限制:

	{
		{"session_timeout", PGC_USERSET, CLIENT_CONN_STATEMENT,
			gettext_noop("Sets the maximum allowed session time."),
			gettext_noop("A value of 0 turns off the timeout."),
			GUC_UNIT_MS
		},
		&SessionTimeout,
		0, 0, INT_MAX,
		NULL, NULL, NULL
	},

        4、增加会话定时器:为会话定义并注册一个定时器:

typedef enum TimeoutId
{
	/* Predefined timeout reasons */
	STARTUP_PACKET_TIMEOUT,
	DEADLOCK_TIMEOUT,
	LOCK_TIMEOUT,
	STATEMENT_TIMEOUT,
	STANDBY_DEADLOCK_TIMEOUT,
	STANDBY_TIMEOUT,
	STANDBY_LOCK_TIMEOUT,
	IDLE_IN_TRANSACTION_SESSION_TIMEOUT,
	IDLE_SESSION_TIMEOUT,
	SESSION_TIMEOUT, //新加会话定时器

	CLIENT_CONNECTION_CHECK_TIMEOUT,
	/* First user-definable timeout reason */
	USER_TIMEOUT,
	/* Maximum number of timeout reasons */
	MAX_TIMEOUTS = USER_TIMEOUT + 10
} TimeoutId;
	if (!bootstrap)
	{
		RegisterTimeout(DEADLOCK_TIMEOUT, CheckDeadLockAlert);
		RegisterTimeout(STATEMENT_TIMEOUT, StatementTimeoutHandler);
		RegisterTimeout(LOCK_TIMEOUT, LockTimeoutHandler);
		RegisterTimeout(IDLE_IN_TRANSACTION_SESSION_TIMEOUT,
						IdleInTransactionSessionTimeoutHandler);
		RegisterTimeout(IDLE_SESSION_TIMEOUT, IdleSessionTimeoutHandler);
        //在此注册会话定时器
		RegisterTimeout(SESSION_TIMEOUT, SessionTimeoutHandler);

		RegisterTimeout(CLIENT_CONNECTION_CHECK_TIMEOUT, ClientCheckTimeoutHandler);
	}
//定时器事件回调函数
static void
SessionTimeoutHandler(void)
{
	SessionTimeoutPending = true;
	InterruptPending = true;
	SetLatch(MyLatch);
}

        5、启动定时器:在PostgresMain函数中会话进入for循环之前使能定时器:

    if (IsUnderPostmaster){
        if (MyProcPort->hba->session_time > 0)
            SessionTimeout = MyProcPort->hba->session_time;
        /* Start the session timer */
        if (SessionTimeout > 0)
        {
            enable_timeout_after(SESSION_TIMEOUT,
                                SessionTimeout*60000);
        }
    }

        通过IsUnderPostmaster判断是postgres进程模式,以hba文件里针对指定用户的会话时长设置为准,如果hba文件里配置该用户会话时长为0,则取GUC参数的设置,最终如果设置的会话时长大于0则使能定时器,会话时长参数设置以分钟为单位;

        6、结束会话:在事件处理函数ProcessInterrupts里,增加对会话时长定时器的事件处理,在此为结束会话:

	if (ClientConnectionLost)
	{
		QueryCancelPending = false; /* lost connection trumps QueryCancel */
		LockErrorCleanup();
		/* don't send to client, we already know the connection to be dead. */
		whereToSendOutput = DestNone;
		ereport(FATAL,
				(errcode(ERRCODE_CONNECTION_FAILURE),
				 errmsg("connection to client lost")));
	}

        7、测试:pg_hba.conf文件中配置所有host登录的用户会话时长为1分钟:

# TYPE  DATABASE        USER            ADDRESS        SESSION-TIME    TIME-RANGE         METHOD

# "local" is for Unix domain socket connections only
local   all             all                                     trust
# IPv4 local connections:
host    all             all             127.0.0.1/32  1  "MonTueWedThuFri|08:00:00-20:00:00,SatSun|10:00:00-16:00:00"     trust
# IPv6 local connections:
host    all             all             ::1/128   120    "MonTueWedThuFri|08:00:00-20:00:00,SatSun|10:00:00-16:00:00"          trust
# Allow replication connections from localhost, by a user with the
# replication privilege.
local   replication     all                                     trust
host    replication     all             127.0.0.1/32  120    "MonTueWedThuFri|08:00:00-20:00:00,SatSun|10:00:00-16:00:00"       trust
host    replication     all             ::1/128     120      "MonTueWedThuFri|08:00:00-20:00:00,SatSun|10:00:00-16:00:00"      trust

        登录测试:

postgres=# select now();
              now
-------------------------------
 2024-07-05 09:52:12.870426+08
(1 row)

postgres=# select now();
              now
-------------------------------
 2024-07-05 09:52:15.931039+08
(1 row)

postgres=# select now();
              now
-------------------------------
 2024-07-05 09:53:04.816819+08
(1 row)

postgres=# select now();
FATAL:  terminating connection due to session timeout
server closed the connection unexpectedly
        This probably means the server terminated abnormally
        before or while processing the request.
The connection to the server was lost. Attempting reset: Succeeded.

         登录一分钟后会话被中断,中断原因为:terminating connection due to session timeout

  • 10
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值