PostgreSQL如何实现按日期定时锁定用户账户(非密码过期)
PostgreSQL 可以实现基于指定日期直接锁定用户账户的需求,完全不需要依赖密码过期机制,核心是利用数据库原生的NOLOGIN角色属性配合定时任务完成,具体实现如下:
核心实现原理
PostgreSQL 原生角色体系自带NOLOGIN权限控制位,只要给对应账号设置该属性,账号会直接失去建立数据库连接的权限,无论输入的密码是否正确、密码是否在有效期内,都无法登录实例,和密码过期机制完全独立,完全匹配直接锁定账户的需求。
具体操作步骤
- 创建禁用计划配置表
用于存储所有预设的账户禁用规则,记录待禁用账号、计划禁用时间、执行状态,方便管理和审计:
- 创建禁用计划配置表
CREATE TABLE user_disable_schedule ( schedule_id serial PRIMARY KEY, target_role name NOT NULL, disable_at timestamptz NOT NULL, is_executed boolean NOT NULL DEFAULT false, remark text, create_time timestamptz NOT NULL DEFAULT now() );
- 创建自动禁用执行函数
函数会自动扫描到点未执行的禁用计划,批量给目标账号打上NOLOGIN标记,同时更新执行状态避免重复操作。注意函数需要由超级用户创建,且加上SECURITY DEFINER标记保证权限足够:
- 创建自动禁用执行函数
CREATE OR REPLACE FUNCTION auto_disable_expired_users() RETURNS void AS $$ DECLARE schedule_rec record; BEGIN FOR schedule_rec IN SELECT target_role, disable_at FROM user_disable_schedule WHERE is_executed = false AND disable_at <= now() LOOP -- 仅对存在、且当前拥有登录权限的角色执行操作,避免无效报错 IF EXISTS ( SELECT 1 FROM pg_roles WHERE rolname = schedule_rec.target_role AND rolcanlogin = true ) THEN EXECUTE format('ALTER ROLE %I NOLOGIN', schedule_rec.target_role); END IF; -- 标记当前计划已执行 UPDATE user_disable_schedule SET is_executed = true WHERE target_role = schedule_rec.target_role AND disable_at = schedule_rec.disable_at; END LOOP; END; $$ LANGUAGE plpgsql SECURITY DEFINER;
- 配置定时触发规则
可以根据实际环境选两种触发方式,不需要额外业务代码介入:
- 方式一:使用
pg_cron数据库内置定时任务(推荐,适合云数据库、不想操作操作系统定时任务的场景)
先安装扩展(超级用户执行),再配置每分钟扫描一次待执行的禁用计划即可:
CREATE EXTENSION IF NOT EXISTS pg_cron; -- 配置每分钟执行一次检查 SELECT cron.schedule('* * * * *', 'SELECT auto_disable_expired_users()');- 方式二:使用操作系统crontab定时任务(适合自建数据库,不想安装扩展的场景)
切换到操作系统的postgres用户,编辑crontab添加以下规则,同样每分钟执行一次检查:
* * * * * psql -d 你的业务库名 -c "SELECT auto_disable_expired_users()" >/dev/null 2>&1- 配置定时触发规则
使用方式
需要给某个账号设置定时禁用时,直接往配置表插入记录即可,比如要给用户test_user设置在2024-12-31 23:59:59自动禁用,执行以下SQL:
INSERT INTO user_disable_schedule(target_role, disable_at, remark) VALUES ('test_user', '2024-12-31 23:59:59+8', '测试账号到期自动锁定');
方案特点
- 锁定逻辑完全独立于密码过期机制,直接收回登录权限,无绕过可能
- 支持批量配置任意时间点的禁用计划,后续调整计划只需要修改配置表记录即可
- 所有操作留痕,满足审计需求
- 后续需要恢复账号权限时,只需要超级用户执行
ALTER ROLE 对应用户名 LOGIN即可立即恢复登录能力
注意:创建函数时使用format('%I', 变量)是为了自动转义角色名,避免特殊字符导致的SQL注入或者语法错误,不要直接拼接SQL字符串。
内容的提问来源于stack exchange,提问作者Daniil Aksenov
相关产品推荐
相关产品推荐

