grant select/insert/update/delete on schema.table_name to user_name 是最细粒度的dml权限控制,须用全限定名,不自动继承,列级权限仅限insert/update/select字段列表,with grant option支持级联回收但不可跨schema转授。

GRANT SELECT/INSERT/UPDATE/DELETE ON table_name TO user_name 的实际效果
直接对单个表授予 SELECT、INSERT、UPDATE 或 DELETE 权限,是最细粒度的 DML 控制方式。它不依赖角色,权限只作用于指定表,且不会自动继承到新创建的同名表或分区表上。
常见错误现象:执行 GRANT SELECT ON emp TO scott 后,scott 仍报错 ORA-00942: table or view does not exist——这通常是因为没加 schema 前缀,正确写法是 GRANT SELECT ON hr.emp TO scott(假设表属于 hr 用户)。
使用场景包括:外包人员只读某张报表表、ETL 账号仅允许向 staging 表插入、审计账号禁止修改但可查历史快照表。
- 必须用全限定名:
schema.table_name,否则 Oracle 默认在当前用户下找表 - 若目标表后续被
TRUNCATE或DROP再重建,权限不会自动恢复,需重新授权 - 该授权不包含
INDEX、COMMENT等对象权限,也不影响关联视图的访问
用 WITH GRANT OPTION 实现权限转授时的关键约束
WITH GRANT OPTION 允许被授权者把同一权限再授给其他人,但它不是“无限传递”——Oracle 会记录授权链,且一旦上游权限被回收,下游所有通过该链获得的权限将立即失效(级联回收)。
容易踩的坑:DBA 给 app_user 授了 GRANT UPDATE ON sales.orders TO app_user WITH GRANT OPTION,结果 app_user 又转授给 dev1;之后 DBA 执行 REVOKE UPDATE ON sales.orders FROM app_user,dev1 的更新权限就立刻不可用了,但不会报错,而是执行 UPDATE 时提示 ORA-01031: insufficient privileges。
- 不能跨 schema 转授:即使
app_user有WITH GRANT OPTION,也不能把sales.orders的权限转授给hr下的表 -
WITH GRANT OPTION不适用于系统权限(如SELECT ANY TABLE) - 查询谁拥有转授权,可用
SELECT * FROM DBA_TAB_PRIVS WHERE GRANTABLE = 'YES'
列级权限(COLUMN PRIVILEGES)能解决哪些真问题
Oracle 支持对表中特定列授权,例如 GRANT UPDATE(salary, bonus) ON hr.employees TO payroll_user。这不是语法糖,而是生产中控制敏感字段修改范围的有效手段。
典型使用场景:薪酬系统账号只能改 salary 和 bonus,但不能碰 ssn、bank_account;HR 招聘账号可插入 name、email,但不能写入 hire_date(由入职流程自动填充)。
注意:列级 SELECT 权限不阻止用户通过函数或视图间接获取该列(比如 SELECT COUNT(*) FROM hr.employees 不需要列权限,但 SELECT ssn FROM hr.employees 会触发检查)。
- 列级权限只适用于
INSERT和UPDATE的字段列表,以及SELECT的投影列 - 如果用户已有整表
SELECT权限,则列级限制无效(高权限覆盖低权限) - 用
DBA_COL_PRIVS视图查列权限:SELECT * FROM DBA_COL_PRIVS WHERE TABLE_NAME = 'EMPLOYEES'
REVOKE 之后权限未立即失效?检查 PUBLIC 和角色继承
执行 REVOKE DELETE ON hr.orders FROM app_user 后,app_user 仍能删数据,大概率是因为该用户还属于某个含 DELETE ANY TABLE 的角色,或者 hr.orders 的 DELETE 权限被授予了 PUBLIC。
真实案例中,DBA 回收了用户对某张审计表的 UPDATE 权限,但应用日志里持续出现 UPDATE audit_log SET status = 'done' 成功记录——最终发现 audit_log 表的 UPDATE 权限早在建表时就被 GRANT UPDATE ON audit_log TO PUBLIC 了。
- 查角色继承路径:
SELECT * FROM DBA_ROLE_PRIVS WHERE GRANTEE = 'APP_USER' - 查 PUBLIC 拥有的对象权限:
SELECT * FROM DBA_TAB_PRIVS WHERE GRANTEE = 'PUBLIC' AND TABLE_NAME = 'ORDERS' - 查用户实际生效权限(含角色和 PUBLIC):
SELECT * FROM DBA_TAB_PRIVS WHERE GRANTEE IN (SELECT GRANTED_ROLE FROM DBA_ROLE_PRIVS WHERE GRANTEE = 'APP_USER' UNION SELECT 'APP_USER' FROM DUAL UNION SELECT 'PUBLIC' FROM DUAL)
权限的“可见性”和“生效时机”常被忽略:回收操作本身秒级完成,但应用连接池里的旧会话仍持有已缓存的权限上下文,直到连接重连或显式执行 ALTER SESSION SET CURRENT_SCHEMA 才会刷新。真正严格的控制,得配合连接管理与最小权限原则一起落地。











