DBMS_RLS 是实现虚拟专用数据库(VPD)的核心机制,而非创建工具;它通过 ADD_POLICY 等过程将行级安全策略动态绑定到表或视图,由优化器运行时自动改写 SQL 加入 WHERE 条件。
oracle 的 dbms_rls 不是创建“虚拟专用数据库”(vpd)的工具,而是实现它的核心机制;所谓 vpd 就是靠它动态加 where 条件来完成行级安全控制的,没有独立的 “vpd_dbms_rls” 这个东西。
为什么 DBMS_RLS 不能直接“创建 VPD”
VPD 是一种安全模型概念,不是可实例化的对象。Oracle 用 DBMS_RLS 包里的一组过程(比如 ADD_POLICY)把策略“挂”到表或视图上,运行时由优化器自动改写 SQL —— 这才是 VPD 的实质。你不会看到一个叫 VPD_DBMS_RLS 的对象,也不会在 DBA_OBJECTS 里查到它。
- 常见错误现象:
ORA-00942: table or view does not exist或PLS-00201: identifier 'VPD_DBMS_RLS' must be declared,说明误把包名当对象名或拼写错误 - 正确入口只有
DBMS_RLS包,必须先GRANT EXECUTE ON DBMS_RLS TO your_user - 策略本身不存储为独立数据库对象,只存在于
DBA_POLICIES视图中,删策略得用DBMS_RLS.DROP_POLICY,不能DROP表或TRUNCATE
怎么用 ADD_POLICY 绑定行级策略到表
关键不是“创建”,而是“绑定”:告诉 Oracle 对哪个表、在什么操作(SELECT/INSERT/UPDATE/DELETE)上、调用哪个函数来生成过滤条件。策略生效后,所有用户(包括 DBA)查该表都会被自动加上约束。
- 使用场景:多租户共享一张订单表
orders,按tenant_id隔离数据 - 必须先写好策略函数,返回
VARCHAR2类型的谓词,例如'tenant_id = SYS_CONTEXT(''APP_CTX'', ''CURRENT_TENANT'')' - 调用
DBMS_RLS.ADD_POLICY时,policy_name要唯一,function_schema和policy_function必须能被策略作用对象的 owner 解析到 - 示例:
BEGIN DBMS_RLS.ADD_POLICY( object_schema => 'SCOTT', object_name => 'ORDERS', policy_name => 'ORDERS_TENANT_POLICY', function_schema => 'SEC_MGR', policy_function => 'get_tenant_filter', statement_types => 'SELECT,INSERT,UPDATE,DELETE', update_check => TRUE ); END;
update_check 和 enable 参数为什么容易被忽略
这两个布尔参数决定策略是否参与 INSERT/UPDATE 的列值校验和是否默认启用,设错会导致权限绕过或报错中断业务。
-
update_check => TRUE:INSERT/UPDATE 时,策略函数生成的谓词也会用于检查新行是否满足条件(比如防止用户伪造tenant_id),不加这个,仅靠 SELECT 策略无法阻止非法写入 -
enable => FALSE:添加策略后默认是启用的;若想先测试再上线,务必显式设为FALSE,否则一执行ADD_POLICY就立刻生效,可能卡住正在跑的报表或应用 - 兼容性影响:12c 及以后支持
CONTEXT_SENSITIVE和SHARED_CONTEXT_SENSITIVE类型策略,但老版本只认STATIC,跨版本迁移时注意策略类型与 Oracle 版本匹配
真正难的不是写那几行 ADD_POLICY,而是策略函数里怎么安全取上下文、怎么避免 SQL 注入、怎么处理 NULL 值和空会话——这些逻辑一旦出错,要么全放行,要么全拦截,而且很难从日志里直接看出问题。










