SQL Server ->> 深入探讨SQL Server 2016新特性之 --- Row-Level Security(行级别安全控制)
SQL Server 2016 CPT3中包含了一个新特性叫Row Level Security(RLS),允许数据库管理员根据业务需要依据客户端执行脚本的一些特性控制客户端能够访问的数据行,比如,我们希望业务部的经理只能查看他所在部门的员工的薪资情况。以往像要实现这样的功能,都是要通过视图里层的逻辑编写来实现。以前某个项目就是这么实现的。或者通过在应用程序层去实现,比如在提交命令到数据库前,通过在查询语句中添加WHERE字句条件来实现数据过滤。这样显然RLS是更加简便的去实现行级别权限控制。
那么它是怎么实现的呢?结合SECURITY POLICY和内联函数
1)SECURITY POLICY作用于表,指明PREDICATE类型以及PREDICATE的参数传入;
2)PREDICATE的定义在内联函数里面;
这么理解吧。PREDICATE的中文意思就是谓语。对SQL Server查询语句执行计划了解的人对这个词肯定不会陌生。筛选条件在执行计划中的表示就是谓语(predicate)。那么这么看其实RLS的实现就是通过表中和某个或者多个字段属性作为引用(参考)条件,而内联函数的作为只不过是封装了谓语(predicate)的定义。定义本身是基于业务而定,所以有开发人员自身去决定。但是作为封装容器,还是需要有人去引用它,那么就是SECURITY POLICY啦。所以SECURITY POLICY成了表与谓语定义的间的纽带。
即:Table <- SECURITY POLICY -> Inline Function
RLS支持两种类型的PREDICATE:1)FILTER PREDICATE;2)BLOCK PREDICATE。两者的区别是什么呢?前者对数据读取操作有效,后者对增删改有效。前者是把违反了谓语的数据行过滤掉,而后者在针对违反了谓语的数据行进行增删改操作的时候触发错误。但是其实FILTER PREDICATE对于更新和删除是生效的,因为更新和删除第一步是先读取数据行嘛。
它们各自的共同点是:
1)内联函数中引用的其他表示不需要考虑是否具有查看权限的;
2)如果SECURITY POLICY的STAT=OFF,那么数据行筛选就失效了;
3)就算是dbo或者db_owner也会受到SECURITY POLICY的影响;
4)因为创建用于SECURITY POLICY的内联函数需要架构绑定,因此如果参考列被更新会触发错误,但是表中的别的栏位不会,同样的修改内联函数也会触发错误;
5)一张表中只可以对某种操作有一个谓语,比如不可以定义多个BEFORE UPDATE的BLOCK PREDICATE;
对于BLOCK PREDICATE:
1)虽然它类似于TRIGGER,但是其实两者是不一样。TRIGGER记录着修改前后的行的栏位数值,而BLOCK PREDICATE只会知道之前或者之后的值(取决于BEFORE UPDATE或者AFTER UPDATE)
2)只要违反了参考栏位的谓语的行才不可以操作;
3)对于BULK INSERT同样生效;
讲了这么多,那么其实RLS相当于用一层WHERE条件暗地里屏蔽掉了一些数据行。
微软的建议:
1)为用于RLS的内联函数和SECURITY POLICY创建单独的schema。
2)要创建SECIRITY POLICY需要有ALTER ANY SECURITY POLICY的权限。
3)避免数据类型转换而造成内联函数内就报错。
4)避免在内联函数中过多的表连接查询。
有一点需要注意:可以对视图应用RLS,但是如果应用了RLS的表被视图引用了,视图是无法创建索引视图的。
RLS和Columnstore索引、CDC、Memory-Optimized Tables是兼容的
MSDN上就给出了一个非常典型的例子,就是员工只能看到他自己的工资,而经理可以看到他下属员工的工资。
第一步先创建好用户和元数据
- CREATE USER Martin WITHOUT LOGIN;
- CREATE USER Sara WITHOUT LOGIN;
- CREATE USER Amy WITHOUT LOGIN;
- CREATE TABLE dbo.Personnel
- (
- EmployeeID INT,
- Name NVARCHAR(100),
- Department NVARCHAR(100),
- Position NVARCHAR(100),
- ReportTo INT,
- Salary FLOAT
- );
- GO
- INSERT INTO dbo.Personnel
- (
- EmployeeID,
- Name,
- Department,
- Position,
- ReportTo,
- Salary
- )
- VALUES
- (
- 1,
- 'Martin',
- 'Accounting',
- 'Manager',
- NULL,
- 10000
- );
- GO
- INSERT INTO dbo.Personnel
- (
- EmployeeID,
- Name,
- Department,
- Position,
- ReportTo,
- Salary
- )
- VALUES
- (
- 2,
- 'Sara',
- 'Accounting',
- 'Accountant',
- 1,
- 5000
- );
- GO
- INSERT INTO dbo.Personnel
- (
- EmployeeID,
- Name,
- Department,
- Position,
- ReportTo,
- Salary
- )
- VALUES
- (
- 3,
- 'Amy',
- 'Accounting',
- 'Accountant',
- 1,
- 3000
- );
- GO
创建Schema和内联函数
- CREATE SCHEMA Security;
- GO
- CREATE FUNCTION Security.fn_SecurityPredicate(@Name AS SYSNAME)
- RETURNS TABLE
- WITH SCHEMABINDING
- AS
- RETURN SELECT 1 AS fn_SecurityPredicate_result
- WHERE @Name = USER_NAME() OR USER_NAME() = 'Martin';
- GO
创建Security Policy
- CREATE SECURITY POLICY SECPOL_PersonnelFilter
- ADD FILTER PREDICATE Security.fn_SecurityPredicate(Name,Position)
- ON dbo.Personnel
- WITH (STATE = ON);
授权给三个数据库用户访问表的权限
- GRANT SELECT ON dbo.Personnel TO [Amy]
- GRANT SELECT ON dbo.Personnel TO [Martin]
- GRANT SELECT ON dbo.Personnel TO [Sara]
开始测试用户Amy能访问多少行数据
- EXECUTE AS USER = 'Amy';
- SELECT USER_NAME(),* FROM dbo.Personnel;
- REVERT;
结果
测试用户Martin
- EXECUTE AS USER = 'Martin';
- SELECT USER_NAME(),* FROM dbo.Personnel;
- REVERT;
结果
测试用户Sara
- EXECUTE AS USER = 'Sara';
- SELECT USER_NAME(),* FROM dbo.Personnel;
- REVERT;
结果
RLS确实是SQL Server 2016的一个很有用处的特性。只是它身上也有一些”缺点“。以上面这个例子来讲,如果你需要实现CEO可以知道整个公司的工资,然后部门经理可以知道整个部门的员工的工资,甚至更复杂点。那么上面那个内联函数就变得复杂了,需要JOIN一些表去判定用户的职位高低来确定他的访问权限范围。而我们都知道在内联函数中加入表引用是很容易引发性能问题的。所以其实像上面这样的办法不是一个好的方案。真正解决办法其实是通过把用户的安全上下文存为一个字符串的变量的形式传入给内联函数,内联函数内部再去利用好这个安全上下文的信息去定义好数据访问的安全控制逻辑。这点在SQL Server 2016提供了一个SESSION_CONTEXT标量函数来配合实现这一方法。这种方法才可能是今后在SQL Server 2016 RTM发布后在现实中去使用RLS的Best Practices。
参考:
ALTER SECURITY POLICY (Transact-SQL)
DROP SECURITY POLICY (Transact-SQL)
sys.security_policies (Transact-SQL)
sys.security_predicates (Transact-SQL)
SQL Server ->> 深入探讨SQL Server 2016新特性之 --- Row-Level Security(行级别安全控制)的更多相关文章
- SQL Server ->> 深入探讨SQL Server 2016新特性之 --- Temporal Table(历史表)
原文:SQL Server ->> 深入探讨SQL Server 2016新特性之 --- Temporal Table(历史表) 作为SQL Server 2016(CTP3.x)的另一 ...
- SQL Server 2016新特性:列存储索引新特性
SQL Server 2016新特性:列存储索引新特性 行存储表可以有一个可更新的列存储索引,之前非聚集的列存储索引是只读的. 非聚集的列存储索引支持筛选条件. 在内存优化表中可以有一个列存储索引,可 ...
- SQL Server 2016新特性:DROP IF EXISTS
原文:SQL Server 2016新特性:DROP IF EXISTS 在我们写T-SQL要删除某个对象(表.存储过程等)时,一般会习惯先用IF语句判断该对象是否存在,然后DROP,比如: 旧 ...
- atitit.Windows Server 2003 2008 2012系统的新特性 attilax 总结
atitit.Windows Server 2003 2008 2012系统的新特性 attilax 总结 1. Windows Server 2008 新特性也可以归纳为4个方面. 1 2. 相 ...
- SQL Server 2016新特性:In-Memory OLTP
存储格式修改 在2014,2016中修改了内存优化表的存储格式,新的格式是序列的并且the database is restarted once during database recovery. ...
- SQL Server ->> SQL Server 2016新特性之 --- Query Store
前言 SQL Server 2016引入新的查询语句性能监控.调试和优化工具/功能 -- Query Store.以前我们发现一条查询语句性能突然下降,我们要去找出问题的所在往往需要通过调用一些DMV ...
- SQL Server ->> SQL Server 2016新特性之 -- Dynamic Data Masking
Dynamic Data Masking是为了防止敏感数据暴露给未经授权的用户,以一种最小开销和维护成本的形式.Dynamic Data Masking用于表的字段,相当于盖住字段数据的一部分.比如一 ...
- SQL Server ->> SQL Server 2016新特性之 -- sp_set_session_context存储过程和SESSION_CONTEXT函数
sp_set_session_context存储过程和SESSION_CONTEXT函数出现在了SQL Server 2016 CTP3.0上.它俩配合起来的作用是sp_set_session_con ...
- sql server 2016新特性 查询存储(Query Store)的性能影响
前段时间给客户处理性能问题,遇到一个新问题, 客户的架构用的是 alwayson ,并且硬件用的是4路96核心,内存1T ,全固态闪存盘,sql server 2016 . 问题 描述 客户经常出现 ...
随机推荐
- 手写JDBC - 数据库、驱动信息存储在配置文件
1. 将数据库.驱动信息存储在配置文件 configure.properties url=jdbc:mysql://localhost:3306/数据库名?serverTimezone=GMT& ...
- es6-函数的扩展
/* * 1:函数参数的默认值 * */ !(() => { function f(x, y = 1, z = 1, g = 1) { console.log(x, y, z, g);//1,1 ...
- Linux C代码 获取IP地址
Ubuntu 16.04下,可编译通过: #include <stdio.h> #include <ifaddrs.h> #include <arpa/inet.h> ...
- Java - 二分法查找(尚学堂第七章数组)
import java.util.Arrays; public class TestBinarySearch { public static void main(String[] args) { in ...
- (转)Mysql数据库管理 表的维护
原文:http://t.dbdao.com/archives/mysql%E6%95%B0%E6%8D%AE%E5%BA%93%E7%AE%A1%E7%90%86-%E8%A1%A8%E7%9A%84 ...
- Request笔记
1 Request 的简介和运行环境 1.HttpServletRequest 概述 我们在创建 Servlet 时会覆盖 service()方法,或 doGet()/doPost(),这些方法都有两 ...
- target与currentTarget区别
target在事件流的目标阶段:currentTarget在事件流的捕获,目标及冒泡阶段.只有当事件流处在目标阶段的时候,两个的指向才是一样的, 而当处于捕获和冒泡阶段的时候,target指向被单击的 ...
- 机器学习——LightGBM
基础概念 LigthGBM是boosting集合模型中的新进成员,它和xgboost一样是对GBDT的高效实现,很多方面会比xgboost表现的更为优秀.原理上它和GBDT及xgboot类似,都采用损 ...
- C语言入门语法
一.数据类型 常量 1.通过预处理声明常量 #include <stdio.h> #define PRICE 100 int main() { printf("价格:%d\n&q ...
- Truncated incorrect DOUBLE value: 'NO_REFUND'
解决办法:Mysql中,如果一个字段是字符串,则一定要加单引号 问题原因: `item_refund_state` ) NOT NULL item_refund_state字段的类型是varchar但 ...