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