C##用户在CDB中创建后默认无法连接PDB,因其缺少PDB级CREATE SESSION权限;必须在每个目标PDB内(先ALTER SESSION SET CONTAINER=pdb_name)显式授予该权限,且PDB需处于OPEN状态。
为什么 C## 用户在 CDB 中创建后,默认进不去 PDB
oracle 12c+ 的多租户架构里,c## 开头的用户是 cdb 公共用户(common user),但“存在”不等于“可用”——它默认没有对任何 pdb 的连接权限,更不会自动拥有 pdb 内的对象访问权。你执行 connect c##user@pdb1 失败,大概率报 ora-01017: invalid username/password 或 ora-01045: user lacks create session privilege,不是密码错,是根本没被授权进这个 pdb。
关键点:公共用户必须显式在每个目标 PDB 中授予 CREATE SESSION,且该授权需在 PDB 内完成(不能只在 CDB$ROOT 做)。
- 必须用
ALTER SESSION SET CONTAINER = pdb_name切到目标 PDB 后再授权 - 授权语句本身要带
CONTAINER = CURRENT(这是默认值,可省略,但意识上不能漏) - 如果 PDB 是
READ WRITE状态但尚未 open,授权会成功,但用户仍连不进去——先确保ALTER PLUGGABLE DATABASE pdb_name OPEN
GRANT CREATE SESSION 必须在 PDB 内执行,不能只在 CDB$ROOT
很多人习惯在 CDB$ROOT 里给 C##USER 授一堆权限,比如 GRANT CONNECT, RESOURCE TO C##USER,但这只影响 CDB 层;PDB 是独立的安全边界,权限不继承。你在 CDB$ROOT 授的 CREATE SESSION,对 PDB 完全无效。
正确流程只有两步,缺一不可:
- 用具有
SET CONTAINER权限的用户(如SYS)登录 CDB$ROOT,执行ALTER SESSION SET CONTAINER = pdb1 - 在当前 session 下执行
GRANT CREATE SESSION TO C##USER(此时CONTAINER = CURRENT自动生效)
验证是否生效:切回该 PDB,用 C##USER 尝试连接:sqlplus C##USER/pass@localhost:1521/pdb1。连得上,说明 session 权限已落地。
PDB 层级对象访问需要额外授权,和 CREATE SESSION 分开处理
拿到 CREATE SESSION 只是进门许可,进屋之后想查表、执行过程,还得有对应 PDB 内的对象权限。这些权限必须在 PDB 内、针对 PDB 内的对象授予——哪怕对象名和 CDB 里一样,也得重授。
常见误区:
- 以为在 CDB$ROOT 给
C##USER授了SELECT_CATALOG_ROLE,就能查 PDB 的DBA_TABLES—— 实际不行,PDB 的数据字典视图是隔离的 - 试图用
GRANT SELECT ON CDB$ROOT.SOME_TABLE TO C##USER让用户访问 PDB 表 —— 路径错误,PDB 表不属于 CDB$ROOT
正确做法举例(在 PDB 内执行):
GRANT SELECT ON scott.emp TO C##USER; GRANT EXECUTE ON pkg_utils TO C##USER;
注意:如果 PDB 启用了本地角色(如自定义 ROLE_PDB_READ),也可以把对象权限先授给角色,再把角色授给 C##USER,但角色本身也必须在 PDB 内创建并授权。
跨 PDB 授权时容易忽略的容器状态与一致性检查
一个 C##USER 要访问多个 PDB,就得在每个 PDB 里单独走一遍授权流程。看似重复,但实际中常卡在三个隐形环节:
- PDB 名称大小写敏感:
ALTER SESSION SET CONTAINER = PDB1和PDB1实际名为pdb1会导致ORA-01109: database not open类似误导性报错 - 某些 PDB 处于
MOUNTED状态而非OPEN:授权语句能执行(语法通过),但用户无法连接,需先ALTER PLUGGABLE DATABASE pdb_name OPEN - 误用
CONTAINER = ALL:对C##USER执行GRANT CREATE SESSION TO C##USER CONTAINER = ALL是非法操作,Oracle 明确禁止该组合,会报ORA-65048: error encountered when processing statement in container
最稳妥的做法,是写个简单脚本循环所有目标 PDB,逐个 ALTER SESSION + GRANT,别图省事用 CONTAINER=ALL 一揽子解决——它真不支持。










