VPD(virtual Private database):虚拟私有数据库,从名字上可能不太好理解。
形象地举个例子:比如我们钢铁行业的数据库,既提供给宝钢使用,又提供给鞍钢使用(这个在实际中是不可能的,此处为了说明问题而已),但是要求宝钢只能访问宝钢的数据,鞍钢只能访问鞍钢的数据。这个时候虽然实际只有一个数据库,但是我们可以看成两个数据库,一个宝钢的数据库,一个鞍钢的数据库,此所谓虚拟私有数据库。
实现上面的关键在于RLS(row level security) 行级权限控制,实现在于通过dbms_rls包,通过add_policy存储过程来实现。
下面举个例子:
--创建演示schema
SQL> create user rls identified by rls;
User created
--创建一个账户表,表示每个客户的账号以及对应余额
SQL> create table rls.accounts(acct_no number not null,cust_id number not null,amount number(15,2));
Table created
SQL> select * from rls.accounts;
ACCT_NO CUST_ID AMOUNT
---------- ---------- -----------------
--插入数据
SQL> insert into rls.accounts values(111111,123,50000);
1 row inserted
SQL> insert into rls.accounts values(222222,123,50000);
1 row inserted
SQL> insert into rls.accounts values(333333,456,50000);
1 row inserted
SQL> insert into rls.accounts values(444444,789,80000);
1 row inserted
SQL> commit;
Commit complete
SQL> select * from rls.accounts;
ACCT_NO CUST_ID AMOUNT
---------- ---------- -----------------
111111 123 50000.00
222222 123 50000.00
333333 456 50000.00
444444 789 80000.00
--创建访问策略表,表中确定了哪个用户对那个账户有什么权利
SQL> create table rls.access_policy(am_name varchar2(20),cust_id number,access_type varchar2(1));
Table created
SQL> insert into rls.access_policy values('SCOTT',123,'S');
1 row inserted
SQL> insert into rls.access_policy values('SCOTT',123,'I');
1 row inserted
SQL> insert into rls.access_policy values('SCOTT',123,'D');
1 row inserted
SQL> insert into rls.access_policy values('SCOTT',123,'U');
1 row inserted
SQL> insert into rls.access_policy values('SCOTT',456,'S');
1 row inserted
SQL> insert into rls.access_policy values('SCOTT',789,'S');
1 row inserted
SQL> insert into rls.access_policy values('HR',456,'I');
1 row inserted
SQL> insert into rls.access_policy values('HR',456,'D');
1 row inserted
SQL> insert into rls.access_policy values('HR',456,'U');
1 row inserted
SQL> insert into rls.access_policy values('HR',456,'S');
1 row inserted
SQL> commit;
Commit complete
SQL> select * from rls.access_policy;
AM_NAME CUST_ID ACCESS_TYPE
-------------------- ---------- -----------
SCOTT 123 S
SCOTT 123 I
SCOTT 123 D
SCOTT 123 U
SCOTT 456 S
SCOTT 789 S
HR 456 I
HR 456 D
HR 456 U
HR 456 S
10 rows selected
--创建查询策略函数,应用上面的rls.access_policy表,只能查看rls.access_policy中存在的对应用户的cust_id
--注意,该函数有两个varchar2类型的参数,不能去掉这2个参数,否则执行查询时报错:ORA-28112: failed to execute policy function
SQL> create or replace function rls.fn_getPolicy_S(p_schema In varchar2,p_object in varchar2) return
2 varchar2 is
3 result varchar2(1000);
4 begin
5 result := 'cust_id in (select cust_id from rls.access_policy where am_name=''' || USER||''' and access_type=''S'')';
6 return result;
7 end fn_getPolicy_S;
8 /
Function created
--检验函数是否正确
SQL> select rls.fn_getPolicy_S('***','XXX') from dual;
FN_GETPOLICY_S('SCOTT','XXX')
------------------------------------------------------------------------------------------
cust_id in (select cust_id from sys.access_policy where am_name='SYS' and access_type='S')
--添加查询策略
begin
-- Call the procedure
dbms_rls.add_policy(object_schema => 'RLS',
object_name => 'ACCOUNTS',
policy_name => 'ACC_S',
function_schema => 'RLS',
policy_function => 'FN_GETPOLICY_S',
statement_types => 'SELECT',
update_check => TRUE,
enable => TRUE
);
end;
--以不同用户登录,查看对应数据,判断查询策略是否起作用;
scott@TESTASM> conn hr/hr;
Connected.
hr@TESTASM> select * from rls.accounts; --hr用户只能查看到456
ACCT_NO CUST_ID AMOUNT
---------- ---------- ----------
333333 456 50000
hr@TESTASM> conn scott/tiger
Connected.
scott@TESTASM> select * from rls.accounts; --scott用户可以查看123、456、789的账户
ACCT_NO CUST_ID AMOUNT
---------- ---------- ----------
222222 123 50000
111111 123 50000
333333 456 50000
444444 789 80000