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 . 问题 描述 客户经常出现 ...
随机推荐
- tess4j 注意事项
依赖: <dependency> <groupId>net.sourceforge.tess4j</groupId> <artifactId>tess4 ...
- Unity QualitySettings.SetQualityLevel 设置质量级别
QualitySettings.SetQualityLevel 设置质量级别 public static void QualitySettings.SetQualityLevel(int index) ...
- Android捕捉图像后在SurfaceView上变形显示问题的处理
我们在Android中经常会使用SurfaceView编写自定义的摄像头,可是有的时候会经常会出现图像的变形,我们就会很郁闷的问这到底是为什么呢?其实这个最根本的原因是SurfaceView和PreV ...
- Windows Server 2008 R2系统上安装SQLServer2012集群(简略)
4台服务器(1台AD.2台SQL服务器.1台iSCSI存储服务器) 9个IP(1个AD的IP.2个SQL服务器的IP.2个心跳IP.1个iSCSI存储服务器的IP.1个集群IP.1个DTC的IP.1个 ...
- Swift函数_inout参数
//无inout参数的函数 func changeName(var name:String){ name = "Hello" println(name) } let payerNa ...
- A space or line break was encountered after the "@" character. Only valid identifiers, keywords, comments, "(" and "{" are valid at the start of a code block and they must occur immediately following
mvc 控制器调用分布视图出错,("A space or line break was encountered after the "@" character. Only ...
- MVC中FileResult 返回类型返回Excel
公司中以前写的导出有问题.原来使用的XML格式字符串拼接然后转化成流输出 action public FileResult ExportJobFair() { try { string name = ...
- Java - Latch和Barrier的区别
之所以把Latch与Barrier放在一起比较是因为他们给人一种相似的感觉. 他们都是阻塞一些行为直至某个事件发生,但Latch是等待某个事件发生,而Barrier是等待线程. 先比较一下JCIP中对 ...
- Spring 数据传入
表单传入 前端代码: <form method="POST" id="user_login_submit"> <div class=" ...
- Reactjs事件处理的三种写法
目录 前言 1. 在回调函数中使用箭头函数 2. 在构造器中绑定this 3. 使用类字段语法 事件参数的传递. 总结 前言 Reactjs中事件处理,与DOM元素处理类似,但也有一些不同的语法. R ...