当前位置: > 数据库 > SQL Server 2016 >

SQL Server 2016新特性:行级别安全控制(Row-Level Security----RLS)

时间:2016-06-05 21:38来源:linux.it.net.cn 作者:IT

  行级别安全控制(Row-Level Security----RLS)能够让我们根据用户执行查询的特性,来控制对数据库表中的数据行进行访问。RSL能够简化应用程序中安全的设计与编写代码,实现对数据行的访问限制。访问限制的逻辑位于数据库层,而不是在应用程序层分离数据。比如,我们希望各部门的经理只能查看他所在部门的员工的薪资情况,医院的护士只能查看自己所负责的病人的状况等。以往像要实现这样的功能,一般要通过视图或者应用程序层去实现,在提交T-SQL之前,通过WHERE条件来实现数据过滤。而SQL Server 2016引入的RLS,则可以更加简便地实现行级别权限控制。RLS是通过创建安全策略(securitypolicy)和内联表值函数(inline table valued functions)来实现的,RLS过滤判定在功能上相当于WHERE语句。

 

        微软的建议:

 

  • 为RSL对象(判定函数及安全策略)创建单独的schema

  • ALTER ANYSECURITY POLICY权限专为高度特权用户(比如安全策略管理者)而设。安全策略管理者对他们所保护的表不需要SELECT权限。

  • 为了避免潜在的运行时错误,在判定函数中要避免类型转换。

  • 只要有可能,要避免在判定函数中使用递归,从而避免性能下降。查询优化器会尝试发现直接的递归,但不能保证发现间接的递归。

  • 为了性能最佳化,判定函数中要避免使用过多的表连接。

     

    下面通过MSDN上的示例,感受一下RLS:

    示例1:创建3个用户,创建一张表并塞入6笔数据,然后为该表创建一个内联表值函数和一个安全策略,最后你可以看到SELECT语句是如何为不同用户过滤数据的。

1.1 创建3个用户

 

  1. CREATE USER Manager WITHOUT LOGIN;  
  2. CREATE USER Sales1 WITHOUT LOGIN;  
  3. CREATE USER Sales2 WITHOUT LOGIN;  

 

 

1.2  建表并塞入6笔数据

 
  1. CREATE TABLE Sales  
  2.     (  
  3.     OrderID int,  
  4.     SalesRep sysname,  
  5.     Product varchar(10),  
  6.     Qty int  
  7.     );  
  8.   
  9.     INSERT Sales VALUES   
  10. (1, 'Sales1', 'Valve', 5),   
  11. (2, 'Sales1', 'Wheel', 2),   
  12. (3, 'Sales1', 'Valve', 4),  
  13. (4, 'Sales2', 'Bracket', 2),   
  14. (5, 'Sales2', 'Wheel', 5),   
  15. (6, 'Sales2', 'Seat', 5);  

 

 

1.3  为每个用户授读权限

 

  1. GRANT SELECT ON Sales TO Manager;  
  2. GRANT SELECT ON Sales TO Sales1;  
  3. GRANT SELECT ON Sales TO Sales2;  

 

 

1.4  创建一个新的schema和一个内联表值函数。

 

 
  1. CREATE SCHEMA Security;  
  2. GO  
  3.   
  4. CREATE FUNCTION Security.fn_securitypredicate(@SalesRep AS sysname)  
  5.     RETURNS TABLE  
  6. WITH SCHEMABINDING  
  7. AS  
  8.     RETURN SELECT 1 AS fn_securitypredicate_result   
  9. WHERE @SalesRep = USER_NAME() OR USER_NAME() = 'Manager';  

 

 

1.5  创建一个安全策略,把内联函数作为过滤判定,状态必须设置为ON

 

  1. CREATE SECURITY POLICY SalesFilter  
  2. ADD FILTER PREDICATE Security.fn_securitypredicate(SalesRep)   
  3. ON dbo.Sales  
  4. WITH (STATE = ON);  

 

 

1.6  测试select,可以看到Sales1和Sales2只能看到他们自己的数据,而Manager可以看到所有数据。


 

1.7  禁用策略后,Sales1和Sales2都能看到所有数据

 

 
  1. ALTER SECURITY POLICY SalesFilter  
  2. WITH (STATE = OFF);  



 

 

示例2:该示例演示了中间层应用程序如何实现连接过滤。应用程序在连接数据库之后,在SESSION_CONTEXT里设置当前的应用程序用户ID,这样,安全策略就能过滤掉该ID不该看到的数据,也能阻止插入用户为错误的ID插入数据。

2.1  建表并塞入6笔数据

 

 
  1. CREATE TABLE Sales (  
  2.     OrderId int,  
  3.     AppUserId int,  
  4.     Product varchar(10),  
  5.     Qty int  
  6. );  
  7.   
  8.   
  9. INSERT Sales VALUES   
  10.     (1, 1, 'Valve', 5),   
  11.     (2, 1, 'Wheel', 2),   
  12.     (3, 1, 'Valve', 4),  
  13.     (4, 2, 'Bracket', 2),   
  14.     (5, 2, 'Wheel', 5),   
  15.     (6, 2, 'Seat', 5);  

 

 

2.2 创建一个低特权用户,应用程式会使用它来连接

 

 
  1. -- Without login only for demo  
  2. CREATE USER AppUser WITHOUT LOGIN;   
  3. GRANT SELECT, INSERT, UPDATE, DELETE ON Sales TO AppUser;  
  4.   
  5. -- Never allow updates on this column  
  6. DENY UPDATE ON Sales(AppUserId) TO AppUser;  

 

 

2.3 创建一个新的schema和判定函数,该函数使用存放在SESSION_CONTEXT里的应用user ID来过滤数据行

 

 
  1. CREATE SCHEMA Security;  
  2. GO  
  3.   
  4. CREATE FUNCTION Security.fn_securitypredicate(@AppUserId int)  
  5.     RETURNS TABLE  
  6.     WITH SCHEMABINDING  
  7. AS  
  8.     RETURN SELECT 1 AS fn_securitypredicate_result  
  9.     WHERE  
  10.         DATABASE_PRINCIPAL_ID() = DATABASE_PRINCIPAL_ID('AppUser')    
  11.         AND CAST(SESSION_CONTEXT(N'UserId') AS int) = @AppUserId;   
  12. GO  

 

 

2.4 创建安全策略

 

 
  1. CREATE SECURITY POLICY Security.SalesFilter  
  2.     ADD FILTER PREDICATE Security.fn_securitypredicate(AppUserId)   
  3.         ON dbo.Sales,  
  4.     ADD BLOCK PREDICATE Security.fn_securitypredicate(AppUserId)   
  5.         ON dbo.Sales AFTER INSERT   
  6.     WITH (STATE = ON);  

 

 

2.5 模拟连接过滤,在SESSION_CONTEXT里设置不同的user ID,然后查询表Sales。实际应用中,应用程序负责在打开连接后,在SESSIION_CONTEXT里设置当前的user ID。

 

 
  1. EXECUTE AS USER = 'AppUser';  
  2. EXEC sp_set_session_context @key=N'UserId', @value=1;  
  3. SELECT * FROM Sales;  
  4. GO  

 
  1. --  Note: @read_only prevents the value from changing again   
  2. --  until the connection is closed (returned to the connection pool)  
  3. EXEC sp_set_session_context @key=N'UserId', @value=2, @read_only=0;   
  4.   
  5. SELECT * FROM Sales;  
  6. GO  
  7.   
  8. INSERT INTO Sales VALUES (7, 1, 'Seat', 12); -- error: blocked from inserting row for the wrong user ID  
  9. GO  
  10.   
  11. REVERT;  
  12. GO  





(责任编辑:IT)
------分隔线----------------------------