vpd策略函数必须返回合法where子句字符串,且需严格匹配对象名、正确设置上下文与dbms_rls参数,否则过滤失效;策略对sys/dba用户默认不生效,测试须用普通业务用户。
oracle vpd(virtual private database)不是“开了就能用”的开关,它依赖策略函数的正确编写、dbms_rls.add_policy 的精准调用,以及对上下文环境的严格控制——漏掉任一环节,行级过滤就会失效,用户仍可绕过限制直接查全表。
策略函数必须返回合法WHERE子句片段,且不能含语法错误
VPD策略函数(如 f_limited_query_t)的返回值会被 Oracle 自动拼接到用户 SQL 的 WHERE 子句后。这意味着:
- 返回值必须是纯字符串,不能带分号或 PL/SQL 块结构
- 不能返回空字符串(
''),应返回NULL或'1=1'表示无限制 - 字段名、表别名需与目标对象实际定义一致;若表使用了同义词或视图,谓词中仍要按基表列名写
- 字符串拼接时注意单引号转义:想返回
username = 'XXF',代码里得写'username = ''XXF''' - 避免在函数中做 DML 或调用非确定性函数(如
SYSDATE),否则可能触发 ORA-20000 错误或导致缓存失效
添加策略时必须指定正确的 schema、object 和 statement_types
DBMS_RLS.add_policy 的参数稍有偏差,策略就无法生效:
-
object_schema是表所在 schema,不是当前登录用户;例如表在SCOTT下,即使你用HR用户执行,也要填'SCOTT' -
object_name区分大小写,若建表时用了双引号(如"Emp"),此处也必须严格匹配 -
statement_types默认只对SELECT生效;如需控制UPDATE或DELETE,必须显式列出,例如'SELECT, UPDATE, DELETE' - 策略名(
policy_name)在同一对象上不可重复;重复执行add_policy会报 ORA-28102 - 若后续要修改策略,不能直接重跑
add_policy,应先用DBMS_RLS.drop_policy清除旧策略
策略对 SYS 和部分内置角色默认不生效,测试时别用 SYSTEM/SYS 登录
Oracle 明确规定:VPD 策略对 SYS、SYSTEM(除非显式授予 EXEMPT ACCESS POLICY 权限)以及拥有 DBA 角色的用户无效。这是设计使然,不是 bug。
- 测试策略是否生效,务必用普通业务用户(如
SCOTT、HR)连接,而不是SYS AS SYSDBA - 若发现“策略没起作用”,先确认当前用户是否被豁免;可用
SELECT * FROM SESSION_ROLES;检查是否意外拥有了DBA - 策略函数内用
SYS_CONTEXT('USERENV', 'SESSION_USER')获取用户名是安全的,但不要用CURRENT_USER—— 它受角色影响,可能返回代理用户名 - 如果业务需要让某管理员也受控,必须显式收回其
EXEMPT ACCESS POLICY权限(极少见,慎用)
策略启用后,所有访问路径都会被拦截,包括视图、DB Link、物化视图日志
VPD 的强制性体现在它工作在 SQL 解析层,只要最终执行计划触及受保护对象,谓词就会注入:
- 哪怕用户查的是一个基于该表的视图,只要视图定义未显式绕过(如用
WITH CHECK OPTION或硬编码过滤),VPD 仍会叠加生效 - 通过 DB Link 远程查询本地受策略表时,策略依然触发(前提是远程库能访问策略函数)
- 物化视图刷新语句(
REFRESH)也会被策略约束,可能导致刷新失败,需确保刷新用户满足谓词条件 - 导出工具如
expdp默认跳过 VPD 策略(除非指定ACCESS_METHOD=VIEWS_AS_TABLES),所以备份数据 ≠ 导出可见数据 - 函数内若依赖应用设置的
CLIENT_IDENTIFIER或自定义上下文,必须确保应用每次请求前都已调用DBMS_SESSION.SET_IDENTIFIER或SET_CONTEXT
真正难的不是写第一个策略函数,而是当多个策略叠加、跨 schema 关联、或与 Label Security 共存时,谓词的优先级和组合逻辑会变得难以追踪;上线前务必用不同用户、不同 SQL 形式(带 hint、子查询、with)、不同工具(SQL*Plus / JDBC / ODBC)交叉验证。











