一、密码失效
[omm@ol8601 ~]$ gsql -Ucy1 -W'P@ssw0rd' -h172.16.220.100 -p26000 -r -d mydb
gsql ((openGauss 5.0.3 build 89d144c2) compiled at 2024-07-31 20:59:31 commit 0 last mr )
NOTICE : The password has been expired, please change the password.
Non-SSL connection (SSL connection is recommended when requiring high-security)
Type "help" for help
应用程序或gsql经常会出现这样的提示,The password has been expired, please change the password. 提示后以后可以登录,但是部分jdbc登录因为这个提示导致连接失败。
从以下psql代码中也可以看到即使密码过期,psql也只是提示密码过期,不做任何拒绝连接等操作。这段代码的主要功能是检查数据库用户的密码是否即将过期,并根据配置的通知时间提前通知用户。它通过执行两个 SQL 查询获取密码的有效期和通知时间,然后根据这些信息判断是否需要通知用户。如果密码已经过期或即将过期,会输出相应的通知信息。
#ifndef ENABLE_LITE_MODE
/* 当密码即将过期时显示通知消息。 */
static void show_password_notify(PGconn* conn)
{
const char* stmt1 = "SELECT pg_catalog.intervaltonum(pg_catalog.gs_password_deadline())"; /* 查询密码有效期的SQL语句 */
const char* stmt2 = "SELECT pg_catalog.gs_password_notifytime()"; /* 查询密码通知时间的SQL语句 */
char* date1 = NULL; /* 用于存储密码有效期的字符串 */
char* date2 = NULL; /* 用于存储密码通知时间的字符串 */
double day1 = 0; /* 用于存储密码有效期的天数(浮点型) */
int day2 = 0; /* 用于存储密码通知时间的天数(整型) */
int leftday = 0; /* 用于存储剩余天数 */
PGresult* res1 = NULL; /* 用于存储第一个查询的结果 */
PGresult* res2 = NULL; /* 用于存储第二个查询的结果 */
bool checkornot = true; /* 标志是否需要检查 */
ExecStatusType resStatus; /* 用于存储查询结果的状态 */
/* 从服务器获取密码过期时间。 */
res1 = PQexec(conn, stmt1); /* 执行查询密码有效期的SQL语句 */
resStatus = PQresultStatus(res1); /* 获取查询结果的状态 */
if (resStatus != PGRES_TUPLES_OK) { /* 如果查询结果状态不是成功(PGRES_TUPLES_OK表示查询成功并返回了数据) */
fprintf(stderr, "execute get deadline failed. resStatus=%d \n", resStatus); /* 输出错误信息到标准错误流 */
/* 上面的错误信息不足以定位问题。 */
if (PQerrorMessage(conn) != NULL) /* 如果连接有错误信息 */
fprintf(stderr, "ERROR: %s\n", PQerrorMessage(conn)); /* 输出错误信息到标准错误流 */
PQclear(res1); /* 清理查询结果 */
PQfinish(conn); /* 关闭数据库连接 */
exit(1); /* 退出程序 */
}
/* 从服务器获取密码通知时间。 */
res2 = PQexec(conn, stmt2); /* 执行查询密码通知时间的SQL语句 */
resStatus = PQresultStatus(res2); /* 获取查询结果的状态 */
if (resStatus != PGRES_TUPLES_OK) { /* 如果查询结果状态不是成功 */
fprintf(stderr, "execute get notice time failed. resStatus=%d \n", resStatus); /* 输出错误信息到标准错误流 */
/* 上面的错误信息不足以定位问题。 */
if (PQerrorMessage(conn) != NULL) /* 如果连接有错误信息 */
fprintf(stderr, "ERROR: %s\n", PQerrorMessage(conn)); /* 输出错误信息到标准错误流 */
PQclear(res2); /* 清理查询结果 */
PQfinish(conn); /* 关闭数据库连接 */
exit(1); /* 退出程序 */
}
date1 = pg_strdup(PQgetvalue(res1, 0, 0)); /* 获取查询结果,第0行第0列的值,并复制到date1中 */
date2 = pg_strdup(PQgetvalue(res2, 0, 0)); /* 获取查询结果,第0行第0列的值,并复制到date2中 */
/* 获取有效天数day1、通知天数day2和剩余天数leftday用于计算。 */
day1 = atof(date1); /* 将字符串date1转换为浮点数 */
day2 = atoi(date2); /* 将字符串date2转换为整数 */
leftday = (int)ceil(day1); /* 将day1向上取整并转换为整数 */
/* 如果密码有效期或密码通知时间为零,我们不进行检查。 */
if (day1 == 0 || day2 == 0) {
checkornot = false; /* 设置标志为false */
}
if (checkornot && day1 < 0) {
/* 密码已经过期,通知并什么都不做。 */
printf("NOTICE : The password has been expired, please change the password. \n"); /* 输出通知信息 */
} else if (checkornot && leftday <= day2) { /* 如果需要检查并且剩余天数小于等于通知天数 */
/* 密码过期时间即将到来,我们将通知用户。 */
printf("NOTICE : %d days left before password expired, please change the password. \n", leftday); /* 输出通知信息 */
}
PQclear(res1); /* 清理第一个查询结果 */
PQclear(res2); /* 清理第二个查询结果 */
free((void *)date1); /* 释放date1的内存 */
free((void *)date2); /* 释放date2的内存 */
return; /* 返回 */
}
#endif
以上代码可以看到psql连接到数据库后分别执行
SELECT pg_catalog.intervaltonum(pg_catalog.gs_password_deadline())
SELECT pg_catalog.gs_password_notifytime()
两条sql获取密码有效期和密码日期,那么密码其中password_notify_time为固定参数,那么密码的有效期限主要来自与gs_password_deadline函数获取的值。
二、密码函数
搜索gs_password_deadline关键字,可以发现gs_password_deadline函数定义在user.app文件中,如下
Datum gs_password_deadline(PG_FUNCTION_ARGS)
{
Oid roleid = GetCurrentUserId(); /* 获取当前用户的OID(对象标识符) */
TimestampTz CurrentPwdtime = GetUserCurrentPwdtime(roleid); /* 获取当前用户的密码创建时间 */
TimestampTz NowTime = GetCurrentTimestamp(); /* 获取当前系统时间 */
Datum FromTimeDatum; /* 用于存储计算结果的Datum类型变量 */
TimestampTz FromTime; /* 用于存储计算结果的时间戳变量 */
Interval TimeSpan; /* 用于存储时间间隔的变量 */
Datum DatumLeftSpan; /* 用于存储剩余时间间隔的Datum类型变量 */
Interval* LeftSpan = (Interval*)palloc0(sizeof(Interval)); /* 分配一个Interval类型的内存空间 */
/* 如果密码生效时间未设置(为0)或密码未启用(CurrentPwdtime 为0),直接返回空。 */
if (u_sess->attr.attr_security.Password_effect_time == 0 || CurrentPwdtime == 0)
PG_RETURN_INTERVAL_P(LeftSpan);
/* 将密码生效时间转换为时间间隔。 */
TimeSpan.month = 0; /* 时间间隔的月数设为0 */
TimeSpan.day = (int)floor(u_sess->attr.attr_security.Password_effect_time); /* 时间间隔的天数设为密码生效时间的整数部分 */
#ifdef HAVE_INT64_TIMESTAMP /* 如果编译时支持int64时间戳 */
TimeSpan.time = (u_sess->attr.attr_security.Password_effect_time - TimeSpan.day) * HOURS_PER_DAY * SECS_PER_HOUR *
USECS_PER_SEC; /* 时间间隔的微秒数设为密码生效时间的小数部分转换为微秒 */
#else
TimeSpan.time = (u_sess->attr.attr_security.Password_effect_time - TimeSpan.day) * HOURS_PER_DAY * SECS_PER_HOUR; /* 时间间隔的秒数设为密码生效时间的小数部分转换为秒 */
#endif
/* 计算密码应该被更改的最晚时间。 */
FromTimeDatum =
DirectFunctionCall2(timestamptz_mi_interval, TimestampGetDatum(NowTime), PointerGetDatum(&TimeSpan)); /* 调用函数计算当前时间减去时间间隔 */
FromTime = DatumGetTimestampTz(FromTimeDatum); /* 将计算结果转换为时间戳类型 */
/* 计算密码到期前的时间。 */
DatumLeftSpan = DirectFunctionCall2(timestamp_mi, CurrentPwdtime, FromTime); /* 调用函数计算密码创建时间减去最晚更改时间 */
LeftSpan = DatumGetIntervalP(DatumLeftSpan); /* 将计算结果转换为Interval类型 */
PG_RETURN_INTERVAL_P(LeftSpan); /* 返回剩余时间间隔 */
}
可以看到密码过期时间是由GetUserCurrentPwdtime函数获取密码创建时间,通过与Password_effect_time对比获取有效时间。检查Password_effect_time函数
/* 获取用户的最新密码更改时间。 */
TimestampTz GetUserCurrentPwdtime(Oid roleID)
{
TupleDesc pg_auth_history_dsc = NULL; /* 用于存储pg_auth_history表的元数据 */
bool passwordtimeIsNull = false; /* 标志密码时间是否为NULL */
Datum passwordtimeDatum; /* 用于存储密码时间的Datum类型变量 */
TimestampTz passwordTime = 0; /* 用于存储密码时间的时间戳变量 */
ScanKeyData key[1]; /* 用于存储扫描键的数据结构 */
HeapTuple historytupe = NULL; /* 用于存储堆元组的数据结构 */
SysScanDesc scan; /* 用于存储系统扫描描述符的数据结构 */
/* 打开pg_auth_history系统表。 */
Relation pg_auth_history_rel = heap_open(AuthHistoryRelationId, AccessShareLock);
/* 根据roleID扫描pg_auth_history表。 */
ScanKeyInit(&key[0], Anum_pg_auth_history_roloid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(roleID));
scan = systable_beginscan(pg_auth_history_rel, AuthHistoryIndexId, true, SnapshotNow, 1, key);
/* 按照索引的逆序获取元组。 */
while (HeapTupleIsValid(historytupe = systable_getnext_back(scan))) {
pg_auth_history_dsc = RelationGetDescr(pg_auth_history_rel);
passwordtimeDatum =
heap_getattr(historytupe, Anum_pg_auth_history_passwordtime, pg_auth_history_dsc, &passwordtimeIsNull);
/* 获取pg_auth_history元组中的密码时间。 */
if (passwordtimeIsNull || NULL == (void*)passwordtimeDatum) {
ereport(ERROR, (errcode(ERRCODE_INVALID_PASSWORD), errmsg("User's passwordtime in pg_auth_history is 0.")));
} else {
/* 获取最新的密码更改时间。 */
passwordTime = Max(passwordTime, DatumGetTimestampTz(passwordtimeDatum));
}
}
systable_endscan(scan);
heap_close(pg_auth_history_rel, AccessShareLock);
return passwordTime;
}
可以看到密码创建时间来自pg_auth_history表的max(passwordTime)最大值。那么可以知道密码过期时间实际上是由这个max(passwordTime)加password_effect_time得到。
三、锁定
以下账户锁定函数,可以看到账户锁定状态只有通过超级用户锁定或者失败次数到达阈值才会锁定账户,账户锁定状态由pg_user_status记录。
void TryLockAccount(Oid roleID, int extrafails, bool superlock)
{
Relation pg_user_status_rel; /* 用户状态关系 */
TupleDesc pg_user_status_dsc; /* 用户状态元数据描述 */
HeapTuple tuple; /* 用户状态元组 */
HeapTuple new_tuple; /* 新的用户状态元组 */
const char* currentTime; /* 当前时间字符串 */
TimestampTz nowTime; /* 当前时间戳 */
int32 failedcount = 0; /* 失败次数 */
int16 status = 0; /* 用户状态 */
Datum userStatusDatum; /* 用户状态数据 */
bool userStatusIsNull = false; /* 用户状态是否为空 */
Datum user_status_record[Natts_pg_user_status]; /* 用户状态记录 */
bool user_status_record_nulls[Natts_pg_user_status] = {false}; /* 用户状态记录空值标志 */
bool user_status_record_repl[Natts_pg_user_status] = {false}; /* 用户状态记录替换标志 */
/* 如果恢复过程中或服务器处于只读模式,则直接返回 */
if (RecoveryInProgress() || SSIsServerModeReadOnly()) {
return;
}
/* 检查参数是否有效 */
if (!LockAccountParaValid(roleID, extrafails, superlock)) {
return;
}
/* 获取当前时间戳 */
nowTime = GetCurrentTimestamp();
/* 获取用户状态关系 */
pg_user_status_rel = RelationIdGetRelation(UserStatusRelationId);
/* 如果关系有效,则获取用户状态元数据描述 */
if (RelationIsValid(pg_user_status_rel)) {
LockRelationOid(UserStatusRelationId, ShareUpdateExclusiveLock); /* 加锁 */
pgstat_initstats(pg_user_status_rel); /* 初始化统计信息 */
pg_user_status_dsc = RelationGetDescr(pg_user_status_rel); /* 获取元数据描述 */
/* 搜索用户状态元组 */
tuple = SearchSysCache1(USERSTATUSROLEID, PointerGetDatum(roleID));
/* 如果元组有效,则处理用户状态 */
if (HeapTupleIsValid(tuple)) {
/* 初始化用户状态记录 */
errno_t errorno = memset_s(user_status_record, sizeof(user_status_record), 0, sizeof(user_status_record));
securec_check(errorno, "\0", "\0");
errorno = memset_s(user_status_record_nulls, sizeof(user_status_record_nulls), false, sizeof(user_status_record_nulls));
securec_check(errorno, "\0", "\0");
errorno = memset_s(user_status_record_repl, sizeof(user_status_record_repl), false, sizeof(user_status_record_repl));
securec_check(errorno, "\0", "\0");
/* 获取用户状态数据 */
userStatusDatum = heap_getattr(tuple, Anum_pg_user_status_failcount, pg_user_status_dsc, &userStatusIsNull);
if (!(userStatusIsNull || (void*)userStatusDatum == NULL)) {
failedcount = DatumGetInt32(userStatusDatum); /* 获取失败次数 */
} else {
failedcount = 0; /* 如果为空,设置为0 */
}
userStatusDatum = heap_getattr(tuple, Anum_pg_user_status_rolstatus, pg_user_status_dsc, &userStatusIsNull);
if (!(userStatusIsNull || (void*)userStatusDatum == NULL)) {
status = DatumGetInt16(userStatusDatum); /* 获取用户状态 */
} else {
status = UNLOCK_STATUS; /* 如果为空,设置为未锁定状态 */
}
/* 如果超级锁定或失败次数超过阈值,则更新用户状态 */
if (superlock || (extrafails > 0 && failedcount + extrafails >= u_sess->attr.attr_security.Failed_login_attempts)) {
/* 更新失败次数和用户状态 */
user_status_record[Anum_pg_user_status_failcount - 1] = Int32GetDatum(failedcount + extrafails);
user_status_record_repl[Anum_pg_user_status_failcount - 1] = true;
if (superlock) {
user_status_record[Anum_pg_user_status_rolstatus - 1] = Int16GetDatum(SUPERLOCK_STATUS); /* 超级锁定状态 */
user_status_record_repl[Anum_pg_user_status_rolstatus - 1] = true;
} else {
user_status_record[Anum_pg_user_status_rolstatus - 1] = Int16GetDatum(LOCK_STATUS); /* 锁定状态 */
user_status_record_repl[Anum_pg_user_status_rolstatus - 1] = true;
}
/* 更新锁定时间 */
currentTime = timestamptz_to_str(nowTime);
user_status_record[Anum_pg_user_status_locktime - 1] = DirectFunctionCall3(
timestamptz_in, CStringGetDatum(currentTime), ObjectIdGetDatum(InvalidOid), Int32GetDatum(-1));
user_status_record_repl[Anum_pg_user_status_locktime - 1] = true;
} else {
/* 如果未达到锁定条件,仅更新失败次数 */
user_status_record[Anum_pg_user_status_failcount - 1] = Int32GetDatum(failedcount + extrafails);
user_status_record_repl[Anum_pg_user_status_failcount - 1] = true;
}
/* 创建新的用户状态元组 */
new_tuple = (HeapTuple)tableam_tops_modify_tuple(
tuple, pg_user_status_dsc, user_status_record, user_status_record_nulls, user_status_record_repl);
/* 更新用户状态 */
heap_inplace_update(pg_user_status_rel, new_tuple);
CacheInvalidateHeapTupleInplace(pg_user_status_rel, new_tuple);
/* 释放元组 */
tableam_tops_free_tuple(new_tuple);
ReleaseSysCache(tuple);
} else {
/* 如果元组无效,报告错误 */
ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("The tuple of pg_user_status not found.")));
}
/* 接受无效化消息 */
AcceptInvalidationMessages();
(void)GetCurrentCommandId(true);
CommandCounterIncrement();
/* 关闭关系 */
heap_close(pg_user_status_rel, ShareUpdateExclusiveLock);
} else {
/* 如果关系无效,报告错误 */
ereport(WARNING, (errmsg("the relation pg_user_status is invalid")));
}
/* 报告锁定账户消息 */
ReportLockAccountMessage(superlock || (extrafails > 0 && failedcount + extrafails >= u_sess->attr.attr_security.Failed_login_attempts), GetUserNameFromId(roleID));
}
四、密码过期
/* 获取账户密码过期状态 */
PASSWORD_STATUS GetAccountPasswordExpired(Oid roleID)
{
if (roleID == BOOTSTRAP_SUPERUSERID) {
return UNEXPIRED_STATUS; /* 如果是初始用户,返回未过期状态 */
}
if (!OidIsValid(roleID)) {
ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("Invalid roleid in pg_user_status."))); /* 如果角色ID无效,报告错误 */
}
int16 status = 0; /* 密码状态 */
bool userStatusIsNull = false; /* 用户状态是否为空 */
HeapTuple tuple = NULL; /* 元组 */
/* 搜索用户状态元组 */
tuple = SearchSysCache1(USERSTATUSROLEID, PointerGetDatum(roleID));
if (!HeapTupleIsValid(tuple)) {
return UNEXPIRED_STATUS; /* 如果元组无效,返回未过期状态 */
} else {
/* 获取用户状态数据 */
Datum userStatusDatum = SysCacheGetAttr((int)USERSTATUSROLEID, tuple, Anum_pg_user_status_passwordexpired, &userStatusIsNull);
if (!(userStatusIsNull || (void*)userStatusDatum == NULL)) {
status = DatumGetInt16(userStatusDatum); /* 获取密码状态 */
}
ReleaseSysCache(tuple); /* 释放元组 */
}
return (PASSWORD_STATUS)status; /* 返回密码状态 */
}
GetAccountPasswordExpired 函数用于获取指定用户账户的密码过期状态。它通过查询 pg_user_status 系统表中的 passwordexpired 字段来确定密码是否过期。如果用户是初始用户(BOOTSTRAP_SUPERUSERID),则直接返回未过期状态。如果用户角色ID无效或在 pg_user_status 中找不到对应的元组,也会返回未过期状态。
/* 设置账户密码过期状态 */
void SetAccountPasswordExpired(Oid roleID, bool expired)
{
if (roleID == BOOTSTRAP_SUPERUSERID) {
ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("Forbidden to make password expired of the initial account."))); /* 禁止将初始账户的密码设置为过期 */
}
Relation pg_user_status_rel = NULL; /* 用户状态关系 */
TupleDesc pg_user_status_dsc = NULL; /* 用户状态元数据描述 */
HeapTuple new_tuple = NULL; /* 新的用户状态元组 */
Datum userStatusRecord[Natts_pg_user_status] = {0}; /* 用户状态记录 */
bool user_status_record_nulls[Natts_pg_user_status] = {false}; /* 用户状态记录空值标志 */
bool user_status_record_repl[Natts_pg_user_status] = {false}; /* 用户状态记录替换标志 */
pg_user_status_rel = RelationIdGetRelation(UserStatusRelationId); /* 获取用户状态关系 */
if (RelationIsValid(pg_user_status_rel)) { /* 如果关系有效 */
LockRelationOid(UserStatusRelationId, ShareUpdateExclusiveLock); /* 加锁 */
pgstat_initstats(pg_user_status_rel); /* 初始化统计信息 */
pg_user_status_dsc = RelationGetDescr(pg_user_status_rel); /* 获取元数据描述 */
HeapTuple tuple = SearchSysCache1(USERSTATUSROLEID, PointerGetDatum(roleID)); /* 搜索用户状态元组 */
if (!HeapTupleIsValid(tuple)) { /* 如果元组无效 */
ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("The roleid of pg_user_status not found."))); /* 报告错误:未找到 pg_user_status 的 roleid */
} else {
/* 初始化用户状态记录 */
userStatusRecord[Anum_pg_user_status_passwordexpired - 1] = Int16GetDatum(expired ? EXPIRED_STATUS : UNEXPIRED_STATUS); /* 设置密码过期状态 */
user_status_record_repl[Anum_pg_user_status_passwordexpired - 1] = true; /* 标记为替换 */
new_tuple = heap_modify_tuple(tuple, pg_user_status_dsc, userStatusRecord, user_status_record_nulls, user_status_record_repl); /* 创建新的用户状态元组 */
simple_heap_update(pg_user_status_rel, &new_tuple->t_self, new_tuple); /* 更新用户状态 */
CatalogUpdateIndexes(pg_user_status_rel, new_tuple); /* 更新索引 */
heap_freetuple_ext(new_tuple); /* 释放元组 */
ReleaseSysCache(tuple); /* 释放系统缓存 */
}
AcceptInvalidationMessages(); /* 接受无效化消息 */
(void)GetCurrentCommandId(true); /* 获取当前命令 ID */
CommandCounterIncrement(); /* 命令计数器加一 */
heap_close(pg_user_status_rel, ShareUpdateExclusiveLock); /* 关闭关系 */
} else {
ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("the relation pg_user_status is invalid"))); /* 报告错误:pg_user_status 关系无效 */
}
}
SetAccountPasswordExpired 函数用于设置指定用户账户的密码过期状态。它通过更新 pg_user_status 系统表中的 passwordexpired 字段来实现。如果用户是初始用户(BOOTSTRAP_SUPERUSERID),则禁止设置密码过期。如果用户角色ID无效或在 pg_user_status 中找不到对应的元组,也会报告错误。
五、调整策略
通过以上分析可以知道,密码有效期计算方式为GetUserCurrentPwdtime函数得出的密码创建日期(pg_auth_history.passwordtime)与固定参数password_effect_time有关。那么可以通过调整password_effect_time参数或者pg_auth_history.passwordtime。现在测试调整pg_auth_history.passwordtime。
1、创建用户并查询
openGauss=# select roloid,passwordtime from pg_auth_history;
roloid | passwordtime
--------+-------------------------------
10 | 2024-11-19 10:02:36.08697+08
24626 | 2024-12-12 10:38:09.834435+08
24630 | 2024-12-28 03:04:20.393433+08
24638 | 2024-12-19 00:00:23.309906+08
32840 | 2025-02-06 16:27:14.698388+08
(5 rows)
openGauss=# select usename,usesysid from pg_user;
usename | usesysid
---------+----------
omm | 10
test | 24626
u1 | 32840
cy | 24630
cy1 | 24638
(5 rows)
openGauss=# show password_effect_time ;
password_effect_time
----------------------
10
(1 row)
openGauss=# show password_notify_time ;
password_notify_time
----------------------
7
(1 row)
2、调整系统日志
[root@ol8601 ~]# date -s '2025-02-27 13:00:00'
[omm@ol8601 src]$ gsql -Uu1 -W'P@ssw0rd' -h172.16.220.100 -p26000 -r -d mydb
gsql ((openGauss 5.0.3 build 89d144c2) compiled at 2024-07-31 20:59:31 commit 0 last mr )
NOTICE : The password has been expired, please change the password.
Non-SSL connection (SSL connection is recommended when requiring high-security)
Type "help" for help.
此时登录数据库已经可以看到提示密码有效期过期,依然可以正常登录
调整pg_auth_history.passwordtim
openGauss=# update pg_auth_history set passwordtime=now() where roloid=32844;
UPDATE 1
openGauss=# select roloid,passwordtime from pg_auth_history;
roloid | passwordtime
--------+-------------------------------
10 | 2024-11-19 10:02:36.08697+08
24626 | 2024-12-12 10:38:09.834435+08
24630 | 2024-12-28 03:04:20.393433+08
24638 | 2024-12-19 00:00:23.309906+08
32844 | 2025-02-27 13:12:10.710969+08
32840 | 2025-02-27 14:56:56.835223+08
(6 rows)
可以看到密码日期已经更改
[omm@ol8601 src]$ gsql -Uu1 -W'P@ssw0rd' -h172.16.220.100 -p26000 -r -d mydb
gsql ((openGauss 5.0.3 build 89d144c2) compiled at 2024-07-31 20:59:31 commit 0 last mr )
Non-SSL connection (SSL connection is recommended when requiring high-security)
Type "help" for help.
mydb=>
不在提示密码过期
[omm@ol8601 src]$ gsql -Ucy1 -W'P@ssw0rd' -h172.16.220.100 -p26000 -r -d mydb
gsql ((openGauss 5.0.3 build 89d144c2) compiled at 2024-07-31 20:59:31 commit 0 last mr )
NOTICE : The password has been expired, please change the password.
Non-SSL connection (SSL connection is recommended when requiring high-security)
Type "help" for help.
另外一个用户密码没有修改,登录依然提示修改密码。
总结:对于因为jdbc捕获密码过期无法登录导致无法登录的情况,可以通过调整pg_auth_history.passwordtime规避。




