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。