VPD = Virtual Private Database。同义词有RLS : Row Level Security, FGAC: Fine Grained Access Control。

用于行级访问控制。假设有需求,只有用户'SCOTT'能访问emp表所有记录,其他人只能访问manager以下员工的记录。

CREATE FUNCTION emp_policy(schema_in IN VARCHAR2,object_in IN VARCHAR2)
RETURN VARCHAR2
ISl_return_value VARCHAR2(32767);
BEGINIF SYS_CONTEXT('USERENV', 'SESSION_USER') = 'SCOTT' THENl_return_value := '1=1';ELSEl_return_value := 'JOB NOT IN (''PRESIDENT'',''MANAGER'')';END IF; RETURN l_return_value;
END;BEGINDBMS_RLS.ADD_POLICY (object_schema    => 'SCOTT',object_name      => 'EMP',policy_name      => 'EMP_POLICY',function_schema  => 'SCOTT',policy_function  => 'emp_policy',statement_types  => 'SELECT, INSERT, UPDATE, DELETE');
END;

控制访问权限的函数-emp_policy有特定的声明,如上所示,它的返回值是where语句部分。对于scott.emp表的select/insert/update/delete,Oracle将自动将l_return_value加入where部分。

我们测试一下:

[oracle@odilab ~]$ sqlplus scott/a123456SQL*Plus: Release 11.2.0.1.0 Production on Sun Jan 19 22:52:20 2014Copyright (c) 1982, 2009, Oracle.  All rights reserved.Connected to:
Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - 64bit Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options$scott@ORCL> select * from emp;EMPNO ENAME      JOB              MGR HIREDATE         SAL       COMM     DEPTNO
---------- ---------- --------- ---------- --------- ---------- ---------- ----------7935 HUIYE      PRESIDENT (null)     03-NOV-13       9999 (null)             107369 SMITH      CLERK           7902 17-DEC-80        800 (null)             207499 ALLEN      SALESMAN        7698 20-FEB-81       1600        300         307521 WARD       SALESMAN        7698 22-FEB-81       1250        500         307566 JONES      MANAGER         7839 02-APR-81       2975 (null)             207654 MARTIN     SALESMAN        7698 28-SEP-81       1250       1400         307698 BLAKE      MANAGER         7839 01-MAY-81       2850 (null)             307782 CLARK      MANAGER         7839 09-JUN-81       2450 (null)             107788 SCOTT      ANALYST         7566 19-APR-87       3000 (null)             207839 KING       PRESIDENT (null)     17-NOV-81       5000 (null)             107844 TURNER     SALESMAN        7698 08-SEP-81       1500          0         307876 ADAMS      CLERK           7788 23-MAY-87       1100 (null)             207900 JAMES      CLERK           7698 03-DEC-81        950 (null)             307902 FORD       ANALYST         7566 03-DEC-81       3000 (null)             207934 MILLER     CLERK           7782 23-JAN-82       1300 (null)             1015 rows selected.
$scott@ORCL> connect scott1/a123456
Connected.$scott1@ORCL> select * from scott.emp;EMPNO ENAME      JOB              MGR HIREDATE         SAL       COMM     DEPTNO
---------- ---------- --------- ---------- --------- ---------- ---------- ----------7369 SMITH      CLERK           7902 17-DEC-80        800 (null)             207499 ALLEN      SALESMAN        7698 20-FEB-81       1600        300         307521 WARD       SALESMAN        7698 22-FEB-81       1250        500         307654 MARTIN     SALESMAN        7698 28-SEP-81       1250       1400         307788 SCOTT      ANALYST         7566 19-APR-87       3000 (null)             207844 TURNER     SALESMAN        7698 08-SEP-81       1500          0         307876 ADAMS      CLERK           7788 23-MAY-87       1100 (null)             207900 JAMES      CLERK           7698 03-DEC-81        950 (null)             307902 FORD       ANALYST         7566 03-DEC-81       3000 (null)             207934 MILLER     CLERK           7782 23-JAN-82       1300 (null)             1010 rows selected.

我们也可以在session中声明自己的context,并给context赋值,最终由context中的值决定l_return_value。如下所示:

CREATE OR REPLACE CONTEXT MY_CONTEXT USING MY_CONTEXT_PKG;
CREATE OR REPLACE PACKAGE my_context_pkg ISPROCEDURE SET_MAX_DEPT;
END MY_CONTEXT_PKG;CREATE OR REPLACE PACKAGE BODY my_context_pkg ISPROCEDURE set_max_deptASl_max_deptno NUMBER;BEGINCASE SYS_CONTEXT('USERENV', 'SESSION_USER')WHEN 'SCOTT' THEN l_max_deptno := 9999;WHEN 'HR' THEN l_max_deptno := 10;WHEN 'OE' THEN l_max_deptno := 20;ELSE l_max_deptno := 0;END CASE;DBMS_SESSION.SET_CONTEXT('my_context', 'max_deptno', l_max_deptno);END set_max_dept;
END my_context_pkg;CREATE TRIGGER tr_set_max_dept AFTER LOGON ON DATABASE
BEGINscott.my_context_pkg.set_max_dept;
END;CREATE FUNCTION DEPT_POLICY(SCHEMA_IN IN VARCHAR2,OBJECT_IN IN VARCHAR2)
RETURN VARCHAR2
ISl_return_value VARCHAR2 (32767);BEGINCASE SYS_CONTEXT('my_context', 'max_deptno')WHEN 10 THEN l_return_value := 'DEPTNO <= 10';WHEN 20 THEN l_return_value := 'DEPTNO <= 20';WHEN 30 THEN l_return_value := 'DEPTNO <= 30';WHEN 40 THEN l_return_value := 'DEPTNO <= 40';WHEN 9999 THEN l_return_value := '1=1';ELSE l_return_value := '1=0';END IF;RETURN L_RETURN_VALUE;
END MY_POLICY;

查询和删除policy

dba|all|user_policies视图保存了与VPD相关的policy

select object_owner, object_name, policy_name, pf_owner, function, enable, static_policy, policy_type
from all_policies;
DBMS_RLS.DROP_POLICY (object_schema IN VARCHAR2 NULL, object_name in varchar2,policy_name IN VARCHAR2);

可以删除某个policy。

Add_policy的Policy_type参数

  • STATIC - The return value of the policy function is cached and reused repeatedly for an individual object. By definition thereturn value of the policy function must be static.
  • SHARED_STATIC - The same as STATIC but the resulting predicate can be applied to several objects.
  • CONTEXT_SENSITIVE - Used when policy is based around local application context. The result of the policy function is cached and reused. The policy function is only executed again when the value of the application context changes.
  • SHARED_CONTEXT_SENSITIVE - The same as CONTEXT_SENSITIVE but the resulting predicate can be applied to several objects.
  • DYNAMIC - The policy function is executed for every SQL statement.

Static and dynamic policy function被调用次数

create or replace function dept_policy(schema_in in varchar2,object_in in varchar2)
RETURN VARCHAR2
IS
begindbms_application_info.set_client_info(userenv('client_info')+1); RETURN '1=1';
END;exec dbms_rls.drop_policy('SCOTT','DEPT','DEPT_POLICY');
BEGINDBMS_RLS.ADD_POLICY (object_schema    => 'SCOTT',object_name      => 'DEPT',policy_name      => 'DEPT_POLICY',function_schema  => 'SCOTT',policy_function  => 'DEPT_policy',statement_types  => 'SELECT, INSERT, UPDATE, DELETE',policy_type => dbms_rls.dynamic --);
END;
$scott@ORCL> select policy_name, function, enable, static_policy, policy_type from all_policies;POLICY_NAME                    FUNCTION                       ENA STA POLICY_TYPE
------------------------------ ------------------------------ --- --- ------------------------
DEPT_POLICY                    DEPT_POLICY                    YES NO  DYNAMIC
EMP_POLICY                     EMP_POLICY                     YES NO  DYNAMICexec dbms_application_info.set_client_info(0);
select /*my4*/ * from dept;
select userenv('client_info') from dual;

Policy_Type为dynamic时,每次执行这条Query返回20条记录,调用2次policy function - dept_policy。根据Oracle文档每次SQL parse和execution各调用一次policy function。

同样的policy_function,假如在add_policy时policy_type => dbms_rls.static时。同样的Query也会返回20条记录,第一次执行调用一次policy function,以后不再调用,因为policy function的结果缓存在SGA中。这符合我们的预期。

Static

static要求policy function返回值是一个常量,考虑如下VPD配置:

CREATE OR REPLACE FUNCTION EMP_POLICY(P_SCHEMA IN VARCHAR2, P_OBJECT IN VARCHAR2)
RETURN VARCHAR2
IS
BEGINdbms_application_info.set_client_info(userenv('client_info')+1);RETURN 'ename = SYS_CONTEXT (''USERENV'', ''SESSION_USER'')';
END;BEGIN  DBMS_RLS.ADD_POLICY (  object_schema    => 'SCOTT',  object_name      => 'EMP',  policy_name      => 'EMP_POLICY',  function_schema  => 'SCOTT',  POLICY_FUNCTION  => 'emp_policy',  STATEMENT_TYPES  => 'SELECT, INSERT, UPDATE, DELETE',policy_type => dbms_rls.static);
END;
Elapsed: 00:00:00.01
$clark@ORCL> select ename from scott.emp;ENAME
----------
CLARKElapsed: 00:00:00.00
$clark@ORCL> connect scott/xxx
Connected.
$scott@ORCL> select ename from scott.emp;ENAME
----------
SCOTTElapsed: 00:00:00.01
SQL_ID  5nwx8yqg94xdy, child number 1
-------------------------------------
select ename from scott.empPlan hash value: 3956160932--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |       |       |     3 (100)|          |
|*  1 |  TABLE ACCESS FULL| EMP  |     1 |     6 |     3   (0)| 00:00:01 |
--------------------------------------------------------------------------Predicate Information (identified by operation id):
---------------------------------------------------1 - filter("ENAME"=SYS_CONTEXT('USERENV','SESSION_USER'))

结论:这种场景,static vpd policy是可行的。

Column level security

有个需求:emp表中dept=10(总裁们所在部门)的工资信息不希望被查询到。

CREATE OR REPLACE FUNCTION EMP_POLICY(P_SCHEMA IN VARCHAR2, P_OBJECT IN VARCHAR2)
RETURN VARCHAR2
IS
BEGINDBMS_APPLICATION_INFO.SET_CLIENT_INFO(USERENV('client_info')+1);RETURN 'deptno=10';
END;BEGIN  DBMS_RLS.ADD_POLICY (  object_schema    => 'SCOTT',  object_name      => 'EMP',  policy_name      => 'EMP_POLICY',  function_schema  => 'SCOTT',  POLICY_FUNCTION  => 'emp_policy',  STATEMENT_TYPES  => 'SELECT, INSERT, UPDATE, DELETE',POLICY_TYPE => DBMS_RLS.STATIC,sec_relevant_cols => 'sal');
END;
$scott@ORCL> select deptno, ename, sal from emp;DEPTNO ENAME             SAL
---------- ---------- ----------20 SMITH             80020 JONES            297520 SCOTT            300020 ADAMS            110020 FORD             3000Elapsed: 00:00:00.00
$scott@ORCL> select deptno, ename from emp;DEPTNO ENAME
---------- ----------10 HUIYE20 SMITH30 ALLEN30 WARD20 JONES30 MARTIN30 BLAKE10 CLARK20 SCOTT10 KING30 TURNER20 ADAMS30 JAMES20 FORD10 MILLER15 rows selected.Elapsed: 00:00:00.01

可以看到,只要用户试图查找deptno=10的员工的薪水,那么deptno=10的行就不显示。也就是说,用户查询sal时,policy function给出的predicate就会被加到sql中。

Data Masking

CREATE OR REPLACE FUNCTION EMP_POLICY(P_SCHEMA IN VARCHAR2, P_OBJECT IN VARCHAR2)
RETURN VARCHAR2
IS
BEGINDBMS_APPLICATION_INFO.SET_CLIENT_INFO(USERENV('client_info')+1);RETURN 'deptno=10';
END;BEGIN  DBMS_RLS.ADD_POLICY (object_schema    => 'SCOTT',  object_name      => 'EMP',  policy_name      => 'EMP_POLICY',  function_schema  => 'SCOTT',  POLICY_FUNCTION  => 'emp_policy',    POLICY_TYPE => DBMS_RLS.STATIC,SEC_RELEVANT_COLS => 'sal',sec_relevant_cols_opt => DBMS_RLS.ALL_ROWS);
END;$scott@ORCL> select deptno, ename, sal from emp order by deptno;DEPTNO ENAME             SAL
---------- ---------- ----------10 HUIYE            999910 MILLER           130010 KING             500010 CLARK            245020 JONES      (null)20 SMITH      (null)20 FORD       (null)20 ADAMS      (null)20 SCOTT      (null)30 ALLEN      (null)30 TURNER     (null)30 JAMES      (null)30 WARD       (null)30 BLAKE      (null)30 MARTIN     (null)

可以看到deptno=10的员工的薪水照常显示,其他部门的都显示为null。 看来,data masking不作用于policy function返回的predicate包含的数据。

VPD的性能

测试了三种情况:

  • 不使用VPD,SQL手工添加predicate再执行
  • 使用VPD,policy_type=static
  • 使用VPD,policy_type=dynamic

Policy function如下:

create or replace FUNCTION EMP_POLICY_1(P_SCHEMA IN VARCHAR2, P_OBJECT IN VARCHAR2)
RETURN VARCHAR2
IS
BEGIN  dbms_application_info.set_client_info(userenv('client_info')+1);  RETURN 'deptno = SYS_CONTEXT (''my_context'', ''deptno'')';
end;  

application context: my_context.deptno的值由trigger设置,具体方法参考上文。

下面给出三次测试的执行计划:

SQL_ID  cj605kcb4s1t6, child number 0
-------------------------------------
select /*3*/ count(*) from emp2 where
deptno=sys_context('my_context','deptno')Plan hash value: 2889146209------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation          | Name | Starts | E-Rows |E-Bytes| Cost (%CPU)| E-Time   | A-Rows |   A-Time   | Buffers | Reads  |
------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |      1 |        |       | 37905 (100)|          |      1 |00:00:20.14 |     137K|    137K|
|   1 |  SORT AGGREGATE    |      |      1 |      1 |     3 |            |          |      1 |00:00:20.14 |     137K|    137K|
|*  2 |   TABLE ACCESS FULL| EMP2 |      1 |   2857K|  8370K| 37905   (2)| 00:07:35 |   1998K|00:00:10.93 |     137K|    137K|
------------------------------------------------------------------------------------------------------------------------------Predicate Information (identified by operation id):
---------------------------------------------------2 - filter("DEPTNO"=TO_NUMBER(SYS_CONTEXT('my_context','deptno')))
------------dynamic------------
 SQL_ID 39sknfv3wmb37, child number 0 select /*3*/ count(*) from emp0 Plan hash value: 3474440072 ------------------------------------------------------------------------------------------------------------------------------| Id | Operation
| Name | Starts | E-Rows |E-Bytes| Cost (%CPU)| E-Time | A-Rows | A-Time | Buffers | Reads |------------------------------------------------------------------------------------------------------------------------------| 0 | SELECT STATEMENT | | 1 | | | 3800
(100)| | 1 |00:00:19.91 | 13699 | 13695 || 1 | SORT AGGREGATE | | 1 | 1 | 3 | | | 1 |00:00:19.91 | 13699 | 13695 ||* 2 | TABLE ACCESS FULL| EMP0 | 1 | 1998K| 5853K| 3800 (2)| 00:00:46 | 1998K|00:00:10.33 | 13699 | 13695 |------------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):--------------------------------------------------- 2 - filter("DEPTNO"=TO_NUMBER(SYS_CONTEXT('my_context','deptno'))) 
------------static------------
 SQL_ID b82cfwj1skv3d, child number 0-------------------------------------select
/*4*/ count(*) from emp0 Plan hash value: 3474440072 ------------------------------------------------------------------------------------------------------------------------------| Id | Operation | Name | Starts | E-Rows |E-Bytes| Cost (%CPU)| E-Time | A-Rows
| A-Time | Buffers | Reads |------------------------------------------------------------------------------------------------------------------------------| 0 | SELECT STATEMENT | | 1 | | | 3800 (100)| | 1 |00:00:17.17 | 13699 | 13695 || 1 | SORT AGGREGATE
| | 1 | 1 | 3 | | | 1 |00:00:17.17 | 13699 | 13695 ||* 2 | TABLE ACCESS FULL| EMP0 | 1 | 1998K| 5853K| 3800 (2)| 00:00:46 | 1998K|00:00:08.90 | 13699 | 13695 |------------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):--------------------------------------------------- 2 - filter("DEPTNO"=TO_NUMBER(SYS_CONTEXT('my_context','deptno')))

可以看到性能差异很小

Oracle VPD相关推荐

  1. oracle vpd策略,oracle vpd 策略查询

    Oracle VPD策略示例 - abce - 博客园 2015年12月14日 Oracle VPD策略示例 1.未创建前使用oe用户登录查询: + View Code + View Code 2.创 ...

  2. ORACLE VPD方案

    ORACLE VPD方案DROP USER VPD CASCADE; DROP ROLE R_VPD; CREATE USER VPD IDENTIFIED BY VPD; CREATE ROLE R ...

  3. 【VPD】使用Oracle VPD(Virtual Private Database)限制用户获取数据的范围

    VPD全称Virtual Private Database,这个技术提供了对数据库信息的细粒度访问控制.关于VPD的更多描述性信息可通过Oracle官方文档获得:http://download.ora ...

  4. [精]Oracle VPD详解(虚拟专用数据库)

    所谓虚拟专用数据库(VPD)指的是,通过在数据库里进行配置,从而让不同的用户只能查看某 个表里的部分数据.VPD分为以下两个级别. 行级别:在该级别下,可以控制某些用户只能查看到某些数据行.比如,对于 ...

  5. 利用Oracle VPD实现行级安全保护(二)

    通过VPD策略的设置,David只能够查看部门60和80的记录. SQL> conn david Enter password:  Connected. SQL> select emplo ...

  6. oracle访问控制策略查看,ORACLE 安全访问策略VPD与ORA-28132

    今天下午,开发同事反馈,某地市的医保oracle数据库执行存储过程报错,报错信息是ORA-28132: ERROR IS:ORA-28132: MERGE INTO 语法不支持安全策略 相关的sql语 ...

  7. 利用Oracle虚拟私有数据库进行整合

    Oracle虚拟私有数据库(Virtual Private Database,下文简称VPD)是Oracle数据库产品中提供的一个安全功能,它能够保证Oracle数据库的多租户特性,与此同时,可以帮助 ...

  8. mysql实现vpd_Oracle Virtual Private Database(VPD) 初体验

    注:本文为原创,作为学习交流使用,转载请标明作者及出处,作者保留追究法律责任的权力. Lumen Su 前几周初略学习了Oracle的VPD技,做了几个试验,也在EBS系统上测试了一下.总结如下,有些 ...

  9. oracle vdp 实现数据权限

    在应用系统开发领域,功能权限和数据权限两层权限体系占到了安全功能性需求的大半.除了在应用程序层面进行处理之外,我们其实还可以从数据库层面实现数据权限访问的. Oracle VPD(Virtual Pr ...

最新文章

  1. ci框架 mysql_CodeIgniter (CI)框架中的数据库查询汇总
  2. 关于工大瑞普Dynamips模拟器
  3. python filter()函数(与map()、reduce()函数用法类似,根据给定函数判断列表元素是True还是False,返回剔除元素后的元素列表)
  4. 华中科技大学主校区论文被《SCI》收录的研究
  5. bilibili弹幕转ass
  6. 201409-1-相邻数对
  7. 宝软网java软件下载_手机游戏怎么下载
  8. 计算机应用基础数制试题及答案,计算机应用基础试题及答案1
  9. 总结:86版五笔输入法
  10. vtk学习教程(一)
  11. 音创a55怎么安装系统_怎么安装系统|教你怎样安装系统教程
  12. html5怎么播放3gp,写了个html5播放视频的video控件,只支持mp4和3gp(android和ios默认支持的格式就写了这个)...
  13. 入门安全测试需要知道什么?需要掌握哪些知识?
  14. 微服务之服务监控篇 ActuatorAdmin
  15. 【12c】扩展数据类型(Extended Data Types)-- MAX_STRING_SIZE
  16. LSA/LSI算法原理和实践
  17. 工程伦理--4.1 解决伦理困境的一般步骤
  18. android 微信分身开发,【技巧】2021安卓手机微信分身方法
  19. 学校信息计算机教案百度文库,人教版信息技术教案 八年级下册_百度文库(45页)-原创力文档...
  20. 为什么要早点进入软件测试行业?现在加入晚了吗?

热门文章

  1. 响应服务器554,服务器554
  2. python自动发邮件报554错误_python3 使用smtplib发送邮件错误554
  3. 更安静的听音乐,摆脱户外噪音干扰,哈氪拾光Pro主动降噪耳机体验
  4. python控制modem的at指令_MODEM AT指令集
  5. android界面设计的解剖,ps cc 2017启动画面的制作解剖
  6. python学习 python实现证件照剪裁、缩放、修改底色
  7. 宋家瑜:做中国的威睿而不是VMware中国
  8. sql 获取日期时分秒_Sql 中获取年月日时分秒的函数
  9. 【复习】系统安全知识点归纳总结
  10. Docker存储卷简述和测试