别用 select any table,它是权限炸弹;应建锁死账户+quota 0+显式授权对象级 select,并过滤临时表、补授物化视图、验证同义词路径及 v$ 视图权限。

直接结论:别用 SELECT ANY TABLE,它不是只读方案,是权限炸弹
生产环境里最常翻车的,就是一句 GRANT SELECT ANY TABLE TO user_name 看似省事,实则埋雷。它让该用户能查所有 schema 下所有普通表(包括未来新建的),且无法按 schema 或表名回收——撤权只能全删或逐个 revoke,运维成本爆炸。真正可控的只读,必须基于对象级授权 + 角色封装。
建用户时必须禁用密码与登录能力
只读用户不是“不能写”,而是“连连都连不上”,除非你明确放行。账户锁死和密码过期是第一道防线:
-
CREATE USER app_ro IDENTIFIED BY "StrongPass123!" DEFAULT TABLESPACE users TEMPORARY TABLESPACE temp QUOTA 0 ON users;——QUOTA 0 ON users防止隐式创建临时段 -
ALTER USER app_ro PASSWORD EXPIRE;—— 强制首次连接时改密(如需交互) -
ALTER USER app_ro ACCOUNT LOCK;—— 关键!避免被误启用;后续只通过ALTER USER app_ro ACCOUNT UNLOCK开放访问 -
GRANT CREATE SESSION TO app_ro;—— 必须显式给,CONNECT角色含历史包袱(如 11g 的UNLIMITED TABLESPACE),不推荐
批量授 SELECT 权限要过滤三类对象
用 dba_tables 生成语句最常用,但容易漏掉关键类型。执行前务必确认以下过滤条件:
- 排除临时表:
AND temporary = 'N'—— 全局临时表(GTTS)不支持跨用户SELECT - 排除物化视图:
dba_tables不包含它们,需额外查dba_mviews并单独GRANT SELECT ON owner.mview_name TO app_ro - 同义词不自动继承权限:如果应用通过同义词访问,必须确保底层表/视图已授权,且用户有
SELECT权限才能解析同义词 - 示例生成语句:
SELECT 'GRANT SELECT ON ' || owner || '.' || table_name || ' TO app_ro;' FROM dba_tables WHERE owner = 'APP_SCHEMA' AND temporary = 'N';
验证只读行为不能只测 SELECT
上线前必须手动触发边界操作,依赖“应该没问题”等于没验:
- 成功:执行
SELECT COUNT(*) FROM app_schema.orders;→ 应返回结果 - 失败:执行
INSERT INTO app_schema.orders VALUES ();→ 必须报ORA-01031: insufficient privileges - 失败:执行
CREATE TABLE test (id NUMBER);→ 报ORA-01031或ORA-01950(因QUOTA 0) - 特别注意:
V$视图默认不可查,若业务依赖(如V$SESSION),需单独授SELECT_CATALOG_ROLE或更细粒度的SELECT权限,但该角色含大量字典视图,慎用
最容易被忽略的是物化视图和同义词路径——它们不在 dba_tables 里,也不自动继承权限,漏一条就可能让只读失效。











