VPD(Virtual Private Database,虚拟专用数据库)是数据库内置的行级安全机制。它通过在 SQL 执行前自动注入过滤条件,实现不同用户看到不同数据的效果,而无需修改应用程序代码。
假设银行所有客户的账户信息都存放在同一张表里。客户A登录时只能看到自己的账户,客户B也只能看到自己的——这就是行级数据隔离。
传统做法:为每个客户创建视图,客户有100万时维护成本极高。
VPD做法:在表上绑定“自动过滤规则”,数据库自动追加 WHERE 条件,无需应用层改动。
| 对比项 | 普通权限控制 | VPD |
|---|---|---|
| 控制粒度 | 表级/列级 | 行级 |
| 实现方式 | GRANT/REVOKE | 策略函数+谓词注入 |
| 应用感知 | 需要应用判断 | 应用无感知,数据库自动过滤 |
| 灵活性 | 静态 | 动态(策略函数可返回不同条件) |
用户执行SQL
↓
数据库调用策略函数 → 返回谓词(WHERE条件字符串)
↓
数据库自动将谓词追加到SQL中
↓
执行改写后的SQL → 返回过滤后的数据
核心组件:
| 项目 | 内容 |
|---|---|
| 数据库版本 | DM8 8.1.4.78 |
| 操作系统 | 麒麟 V10 |
| 关键参数 | ENABLE_RLS=1 |
-- 1. 开启VPD(静态参数,需重启)
SP_SET_PARA_VALUE(2, 'ENABLE_RLS', 1);
-- 重启数据库: systemctl restart DmServiceDMSERVER
-- 2. 确认参数已生效
SELECT PARA_NAME, PARA_VALUE FROM V$DM_INI WHERE PARA_NAME = 'ENABLE_RLS';
-- 预期:ENABLE_RLS | 1
-- 3. 创建测试用户
DROP USER U01_VPD CASCADE;
CREATE USER U01_VPD IDENTIFIED BY "U01_VPD_Abc";
GRANT DBA TO U01_VPD;
CONN U01_VPD/U01_VPD_Abc
CREATE TABLE T01(C1 INT, C2 VARCHAR2(10));
INSERT INTO T01 VALUES(1,'A');
INSERT INTO T01 VALUES(2,'B');
INSERT INTO T01 VALUES(3,'C');
INSERT INTO T01 VALUES(4,'D');
INSERT INTO T01 VALUES(5,'E');
COMMIT;
CONN SYSDBA/Dameng123
-- 策略函数:只允许看到 C1 < 4 的数据
CREATE OR REPLACE FUNCTION FN_POLICY(
P_OWNER IN VARCHAR2, P_OBJECT IN VARCHAR2
) RETURN VARCHAR2 IS
BEGIN
RETURN 'C1 < 4';
END FN_POLICY;
/
-- 绑定策略到表(UPDATE_CHECK=TRUE)
CALL DBMS_RLS.ADD_POLICY(
OBJECT_SCHEMA => 'U01_VPD',
OBJECT_NAME => 'T01',
POLICY_NAME => 'POLICY_01',
FUNCTION_SCHEMA => 'SYSDBA',
POLICY_FUNCTION => 'FN_POLICY',
STATEMENT_TYPES => 'SELECT,INSERT,UPDATE,DELETE,INDEX',
UPDATE_CHECK => TRUE
);
/
CONN U01_VPD/U01_VPD_Abc
-- 普通用户查询(受VPD限制)
SELECT * FROM T01 ORDER BY C1;
行号 C1 C2
---------- ----------- --
1 1 A
2 2 B
3 3 C
CONN SYSDBA/Dameng123
-- SYSDBA查询(绕过VPD,验证物理数据)
SELECT * FROM U01_VPD.T01 ORDER BY C1;
行号 C1 C2
---------- ----------- --
1 1 A
2 2 B
3 3 C
4 4 D
5 5 E
核心验证方法:
执行计划特征:
CONN U01_VPD/U01_VPD_Abc
EXPLAIN SELECT * FROM T01;
1 #NSET2: [1, 1, 64]
2 #PRJT2: [1, 1, 64]; exp_num(3), is_atom(FALSE)
3 #PRJT2: [1, 1, 64]; exp_num(3), is_atom(FALSE)
4 #SLCT2: [1, 1, 64]; T01.C1 < 4 SLCT_PUSHDOWN(TRUE)
5 #CSCN2: [1, 5, 64]; INDEX33555520(T01) NEED_SLCT(TRUE); btr_scan(1)
-- 插入符合策略的数据(C1=3 < 4)
INSERT INTO T01 VALUES(3, 'C');
影响行数 1
-- 插入违反策略的数据(C1=6 >= 4)
INSERT INTO T01 VALUES(6, 'F');
INSERT INTO T01 VALUES(6, 'F');
[-6614]:违反策略检查约束.
原因:UPDATE_CHECK=TRUE 要求新数据必须符合VPD策略。
-- 更新为符合策略的值(C1=2, C2='X')
UPDATE T01 SET C2='X' WHERE C1=2;
影响行数 1
-- 更新为违反策略的值(C1=2 -> C1=5)
UPDATE T01 SET C1=5 WHERE C1=2;
UPDATE T01 SET C1=5 WHERE C1=2;
[-6614]:违反策略检查约束.
-- 删除符合策略的数据(C1=2)
DELETE FROM T01 WHERE C1=2;
影响行数 1
-- 删除不可见的数据(C1=5,不在VPD可见范围内)
DELETE FROM T01 WHERE C1=5;
影响行数 0
| 参数值 | 行为 | 业务场景 |
|---|---|---|
TRUE |
新数据必须符合策略,否则报错 | 严格要求数据合规 |
FALSE |
允许更新为不符合策略的值(更新后不可见) | 软删除、数据归档 |
验证:设置 UPDATE_CHECK=FALSE 后,将数据更新为不符合策略的值会成功,但更新后用户看不到该数据。
VPD 的本质是谓词注入。策略函数返回什么条件,执行计划中就会以相应的形式呈现。简单条件表现为 SLCT2,OR 条件展开为 UNION FOR OR2。无论哪种形式,只要谓词被正确附加,VPD 策略就已生效。
文章
阅读量
获赞
