oracle 19c中权限回收必须严格匹配授予权限时的container子句:全局授权(container=all)须在cdb$root用相同子句回收,pdb内local授权(container=current)只能在该pdb内对应回收,不存在按单个pdb筛选回收的语法。

不能直接回收“特定PDB”的全局权限——回收动作本身没有“按PDB筛选”能力,必须匹配当初授权时的 CONTAINER 子句逻辑。
回收权限必须与授予权限的 CONTAINER 子句严格对应
Oracle 19c 的权限回收不是“删掉某 PDB 的那一份”,而是撤销某次 GRANT 操作的全部生效范围。你回收什么,取决于当初怎么授的:
- 如果当初用
GRANT CONNECT TO c##app CONTAINER = ALL授了全局权限,那么REVOKE CONNECT FROM c##app(不带子句)或REVOKE CONNECT FROM c##app CONTAINER = ALL都会撤销所有 PDB + CDB 的该权限 - 如果当初误在 PDB 内执行了
GRANT CONNECT TO c##app CONTAINER = CURRENT,那这条授权只作用于当前 PDB,回收时也必须用REVOKE CONNECT FROM c##app CONTAINER = CURRENT,且只能在同一个 PDB 中执行 - 不存在“只回收 pdb1 但保留 pdb2”的语法;想实现这种效果,得先确认当初是否分两次、用不同
CONTAINER值授过权
常见错误:在 PDB 里执行回收命令
很多 DBA 切到某个 PDB 后想“只收掉这个库的权限”,于是执行:
ALTER SESSION SET CONTAINER = salespdb; REVOKE CONNECT FROM c##app;
结果发现:c##app 在 salespdb 里依然能登录。原因:
- 这条
REVOKE实际撤销的是在salespdb内部执行过的、CONTAINER = CURRENT的那次授权(如果有的话) - 但
c##app的CONNECT权限大概率来自 CDB$ROOT 的CONTAINER = ALL授权,它不受 PDB 级别的REVOKE影响 - 更糟的是:如果没在
salespdb里授过权,这条语句会报ORA-01927: cannot revoke privileges you did not grant
正确做法:先查清权限来源,再回 CDB$ROOT 回收
执行以下查询,确认 c##app 的 CONNECT 权限是从哪来的:
SELECT GRANTEE, GRANTED_ROLE, COMMON, CON_ID,
DECODE(CONTAINER, 1, 'CDB', 3, 'PDB') AS SCOPE
FROM CDB_ROLE_PRIVS
WHERE GRANTEE = 'C##APP' AND GRANTED_ROLE = 'CONNECT';
关键看 CON_ID 和 COMMON 列:
- 若
CON_ID = 1且COMMON = 'YES'→ 权限来自 CDB$ROOT 的全局授权,必须切回CDB$ROOT执行回收 - 若
CON_ID = 3(假设是 salespdb 的 CON_ID)→ 权限是本地授予的,可在该 PDB 内回收,但需加CONTAINER = CURRENT
确认后,在 CDB$ROOT 执行:
ALTER SESSION SET CONTAINER = CDB$ROOT; REVOKE CONNECT FROM c##app CONTAINER = ALL;
想真正实现“某 PDB 不让登录”,靠回收权限不如换思路
单纯回收 CONNECT 很难精准控制到单个 PDB,因为公共用户默认跨所有容器存在。更可靠的做法是:
- 在目标 PDB 内,用
ALTER USER c##app ACCOUNT LOCK锁定账户(仅对该 PDB 生效) - 或者彻底避免用公共用户登录业务 PDB,改用本地用户 + 数据库链接(
DBLINK)访问跨租户数据 - 若必须保留公共用户但限制访问,可结合
PROXY AUTHENTICATION或细粒度审计策略,在应用层拦截
真正的隔离难点不在“怎么收”,而在于“当初有没有混用 CONTAINER 子句”——一旦在多个上下文重复授权,权限叠加后就很难干净剥离。











