1、问题现象

业务系统连接 Oracle 数据库时失败,页面提示类似:

ORA-65162: The password of the common user has expired

从报错可以看出,这不是网络端口问题,也不是监听服务异常,而是 Oracle common user 密码过期

本次涉及两个 CDB:

MESCDB
UTF8CDB

相关用户为:

C##MESCDBFLINK
C##UTF8CDBFLINK

这类 C## 开头的用户,一般属于 CDB common user


二、确认当前 CDB/PDB 环境

登录数据库服务器后,进入对应实例:

su - oracle
export ORACLE_SID=mescdb1
sqlplus / as sysdba

查看 PDB:

show pdbs;

示例结果:

CON_ID  CON_NAME   OPEN MODE
------  --------   ----------
2       PDB$SEED   READ ONLY
3       MESDB      READ WRITE
5       RZMESDB    READ WRITE

确认当前容器:

show con_name;

如果是 common user,建议在 CDB$ROOT 下处理。

alter session set container=CDB$ROOT;

三、查询用户状态

先通过 cdb_users 查询用户状态:

set lines 200 pages 200
col con_name for a15
col username for a35
col account_status for a30
col expiry_date for a20
col profile for a25
col common for a8

select u.con_id,
       c.name con_name,
       u.username,
       u.account_status,
       u.expiry_date,
       u.profile,
       u.common
from cdb_users u
join v$containers c on u.con_id = c.con_id
where u.username = upper('C##MESCDBFLINK')
order by u.con_id;

排查结果如下:

CON_ID  CON_NAME   USERNAME        ACCOUNT_STATUS  EXPIRY_DATE           PROFILE   COMMON
------  --------   --------------  --------------  -------------------   -------   ------
1       CDB$ROOT   C##MESCDBFLINK  EXPIRED         2026-05-13 09:57:06   DEFAULT   YES
3       MESDB      C##MESCDBFLINK  OPEN                                  DEFAULT   YES
5       RZMESDB    C##MESCDBFLINK  OPEN                                  DEFAULT   YES

这里可以明确看出:

CDB$ROOT 下账号状态为 EXPIRED

虽然 PDB 中显示为 OPEN,但是 common user 在 CDB$ROOT 中密码过期后,连接 PDB 仍然会失败。


四、创建专用 Profile,设置密码永不过期

生产环境不建议直接修改 DEFAULT profile,因为可能影响其他用户。

更稳妥的方式是:单独创建业务账号专用 profile

alter session set container=CDB$ROOT;

先检查 profile 是否存在:

select profile
from dba_profiles
where profile = 'C##APP_PROFILE'
group by profile;

如果不存在,创建:

create profile C##APP_PROFILE limit
  PASSWORD_LIFE_TIME UNLIMITED
  PASSWORD_GRACE_TIME UNLIMITED;

说明:

PASSWORD_LIFE_TIME  UNLIMITED  表示密码永不过期
PASSWORD_GRACE_TIME UNLIMITED  表示不进入密码过期宽限期

五、将 common user 切换到新 Profile

C##MESCDBFLINK 为例:

alter user C##MESCDBFLINK profile C##APP_PROFILE container=all;

然后使用原密码重置并解锁:

alter user C##MESCDBFLINK identified by "原密码" account unlock container=all;

如果密码中包含特殊字符,比如 #,必须使用双引号包起来:

alter user C##MESCDBFLINK identified by "******" account unlock container=all;

注意:
这里建议使用原密码重置,这样业务系统连接配置不用同步修改。


六、验证处理结果

再次查询用户状态:

select u.con_id,
       c.name con_name,
       u.username,
       u.account_status,
       u.expiry_date,
       u.profile,
       u.common
from cdb_users u
join v$containers c on u.con_id = c.con_id
where u.username = 'C##MESCDBFLINK'
order by u.con_id;

正常结果应类似:

CON_ID  CON_NAME   USERNAME        ACCOUNT_STATUS  EXPIRY_DATE  PROFILE          COMMON
------  --------   --------------  --------------  -----------  ---------------  ------
1       CDB$ROOT   C##MESCDBFLINK  OPEN                         C##APP_PROFILE   YES
3       MESDB      C##MESCDBFLINK  OPEN                         C##APP_PROFILE   YES
5       RZMESDB    C##MESCDBFLINK  OPEN                         C##APP_PROFILE   YES

重点看三项:

ACCOUNT_STATUS = OPEN
EXPIRY_DATE    为空
PROFILE        = C##APP_PROFILE

再确认 profile 策略:

select profile, resource_name, limit
from dba_profiles
where profile = 'C##APP_PROFILE'
and resource_name in (
  'PASSWORD_LIFE_TIME',
  'PASSWORD_GRACE_TIME',
  'FAILED_LOGIN_ATTEMPTS',
  'PASSWORD_LOCK_TIME'
)
order by resource_name;

示例结果:

PROFILE          RESOURCE_NAME          LIMIT
---------------  ---------------------  ---------
C##APP_PROFILE   FAILED_LOGIN_ATTEMPTS  DEFAULT
C##APP_PROFILE   PASSWORD_GRACE_TIME    UNLIMITED
C##APP_PROFILE   PASSWORD_LIFE_TIME     UNLIMITED
C##APP_PROFILE   PASSWORD_LOCK_TIME     DEFAULT

这里说明:

密码已经永不过期;
但如果密码连续输错,仍然可能按 DEFAULT 策略锁定。

七、CDB common user 和 PDB local user 的区别

本次处理的两个用户:

C##MESCDBFLINK
C##UTF8CDBFLINK

都属于 common user,特点是:

用户名以 C## 开头
COMMON = YES

这种用户应在:

CDB$ROOT

中处理,并使用:

container=all

例如:

alter user C##MESCDBFLINK profile C##APP_PROFILE container=all;

如果是普通业务用户,例如:

STEELMES
MES_ADMIN
HYTOMES
QMS
LZMES
RZMES

如果查询结果为:

COMMON = NO

则说明它们是 PDB local user,只属于某个具体 PDB。

这类用户不需要在 CDB$ROOT 中修改,直接切换到所属 PDB 处理即可:

alter session set container=MESDB;

create profile APP_PROFILE limit
  PASSWORD_LIFE_TIME UNLIMITED
  PASSWORD_GRACE_TIME UNLIMITED;

alter user STEELMES profile APP_PROFILE;

如果账号已过期或锁定,再用原密码重置解锁:

alter user STEELMES identified by "原密码" account unlock;

八、总结

本次问题的核心原因是:

Oracle CDB common user 在 CDB$ROOT 中密码过期,导致业务系统连接 PDB 失败。

关键排查 SQL:

select u.con_id,
       c.name con_name,
       u.username,
       u.account_status,
       u.expiry_date,
       u.profile,
       u.common
from cdb_users u
join v$containers c on u.con_id = c.con_id
where u.username = upper('用户名')
order by u.con_id;

关键处理思路:

common user:在 CDB$ROOT 中处理,使用 container=all
local user :进入对应 PDB 中处理,不使用 container=all

最终处理方式:

创建专用 profile
设置 PASSWORD_LIFE_TIME 为 UNLIMITED
设置 PASSWORD_GRACE_TIME 为 UNLIMITED
将业务连接用户切换到该 profile
使用原密码重置并解锁账号

一句话总结:
C## 用户看 CDB$ROOT,普通业务用户进 PDB;谁家的账号谁家管,别在生产环境里乱改 DEFAULT。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐