Oracle行级安全(RLS)通过DBMS_RLS包动态绑定策略函数实现,优化器在SQL解析时自动重写语句添加WHERE条件;策略存于DBA_POLICIES视图,非独立数据库实体。
oracle 行级安全(rls)不是靠建个“vpd数据库”实现的,而是通过 dbms_rls 包把策略函数动态挂到表上,让优化器在运行时自动改写 sql 加 where 条件——这才是真实生效机制。
DBMS_RLS.ADD_POLICY 是核心入口,不是创建工具
你不会在 DBA_OBJECTS 里查到叫 VPD_DBMS_RLS 的对象,也不存在独立的“虚拟专用数据库”实体。所有策略都只是逻辑绑定,存于 DBA_POLICIES 视图中。
-
DBMS_RLS.ADD_POLICY的作用是「声明」:对哪个表、哪些操作类型(SELECT/INSERT等)、调用哪个函数生成过滤谓词 - 必须提前
GRANT EXECUTE ON DBMS_RLS TO your_user,否则会报PLS-00201: identifier 'DBMS_RLS' must be declared -
policy_name在同一张表上必须唯一;重复执行ADD_POLICY同名策略会报错,得先DBMS_RLS.DROP_POLICY - 策略函数返回的是字符串(如
'tenant_id = SYS_CONTEXT(''APP_CTX'', ''CURRENT_TENANT'')'),不是布尔值——这点和很多人直觉相反
策略函数必须返回 VARCHAR2 类型的谓词字符串
常见错误是写成返回 BOOLEAN 或直接 RETURN TRUE/FALSE,这会导致 ORA-28100: policy function returned an invalid predicate。
- 函数签名必须是
FUNCTION func_name(p_schema VARCHAR2, p_object VARCHAR2) RETURN VARCHAR2 - 两个参数不可省略,即使不用也要保留(Oracle 强制传入)
- 返回空字符串
''表示“不限制”,返回NULL则整行被过滤掉(慎用) - 函数内不能用
SELECT ... INTO查当前用户信息——应优先用SYS_CONTEXT,比如SYS_CONTEXT('USERENV', 'SESSION_USER')或自定义应用上下文
策略生效后,所有用户(包括 DBA)都受约束
除非显式指定 static_policy => TRUE 并配合 sec_relevant_cols,否则策略对所有会话透明生效——这不是视图或权限控制,是 SQL 解析阶段的重写。
- 普通用户查表时看不到加了什么条件,但执行计划里会出现类似
filter("TENANT_ID"=:B1)的谓词 -
UPDATE_CHECK => TRUE时,插入/更新违反策略的数据会直接报错,而不是静默过滤 - 如果策略函数抛异常(如
NO_DATA_FOUND),整个 SQL 会失败,不是跳过某行 - 使用
sec_relevant_cols可触发列级敏感处理:当查询包含这些列时才启用策略,否则绕过——适合只对部分字段做行过滤的场景
容易被忽略的兼容性与调试点
策略函数里用到的上下文、表、函数,必须能被策略所挂载的表 owner 解析到。例如表在 HR schema,函数在 SEC schema,那么 HR 必须有 EXECUTE 权限且能 resolve 到该函数。
- 调试时别只看
SELECT *,用EXPLAIN PLAN FOR SELECT ...+SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY)看实际谓词是否注入成功 - Oracle 12c 及以后支持
POLICY_TYPE => DBMS_RLS.SHARED_STATIC,可复用谓词提升性能,但要求函数无会话依赖 -
DESC_COLS参数仅用于描述,不影响行为;真正影响执行逻辑的是stmt_type和update_check - 策略不继承:子分区表需单独为每个分区加策略,或使用全局策略(需 Oracle 12.2+)











