SQL Server 逐行访问

Lia*_*amB 12 sql-server row-level-security

我有一个这样结构的表(简化)

Name, EMail, LastLoggedInAt
Run Code Online (Sandbox Code Playgroud)

我在 SQL Server (RemoteUser) 中有一个用户,该用户应该只能看到 LastLoggdInAt 字段不为空的数据(通过选择查询)。

看起来我可以做到这一点?是否可以?

Han*_*non 33

SQL Server 安全模型允许您授予对视图的访问权限,而无需授予对基础表的访问权限。

由于示例代码是展示概念的好方法,请考虑以下内容,以及一个LoginDetails表格和相应的视图:

CREATE TABLE dbo.LoginDetails
(
    Username nvarchar(100) NOT NULL
    , EmailAddress nvarchar(256) NOT NULL
    , LastLoggedInAt datetime NULL
);
GO

CREATE VIEW dbo.LoginDetailsView
AS
SELECT ld.Username
    , ld.EmailAddress
    , ld.LastLoggedInAt
FROM dbo.LoginDetails ld
WHERE ld.LastLoggedInAt IS NOT NULL;
GO
Run Code Online (Sandbox Code Playgroud)

我们将创建一个登录名和一个用户,然后为该用户分配从视图中选择行的权限,而没有任何查看表本身的权限。

CREATE LOGIN RemoteUser 
WITH PASSWORD = '2q1345lkjsadfgsa0(*';

CREATE USER RemoteUser
FOR LOGIN RemoteUser
WITH DEFAULT_SCHEMA = dbo;

GRANT SELECT ON dbo.LoginDetailsView TO RemoteUser;
Run Code Online (Sandbox Code Playgroud)

现在,我们将插入两个测试行:

INSERT INTO dbo.LoginDetails(Username, EmailAddress, LastLoggedInAt)
VALUES ('user x', 'x@y.com', NULL)
    , ('user y', 'y@y.com', GETDATE());
Run Code Online (Sandbox Code Playgroud)

这将测试安全模型。第一条SELECT语句成功,因为它是从视图中进行选择,而第二条SELECT语句失败,因为用户没有直接访问表的权限。

EXECUTE AS LOGIN = 'RemoteUser';

SELECT *
FROM dbo.LoginDetailsView;
Run Code Online (Sandbox Code Playgroud)
?????????????????????????????????????????????????????? ???
? 用户名 ?电子邮件地址 ?LastLoggedInAt ?
?????????????????????????????????????????????????????? ???
? 用户 ? y@y.com ?2018-02-15 07:36:54.490?
?????????????????????????????????????????????????????? ???
SELECT *
FROM dbo.LoginDetails;

REVERT
Run Code Online (Sandbox Code Playgroud)

请注意视图中的结果排除了LastLoggedInAt值所在的行NULL,如您的问题所要求的那样。

SELECT针对基础表的第二条语句返回错误:

消息 229,级别 14,状态 5,第 28 行
对象“LoginDetails”、数据库“tempdb”、架构“dbo”的 SELECT 权限被拒绝。

清理:

DROP USER RemoteUser;
DROP LOGIN RemoteUser;
DROP VIEW dbo.LoginDetailsView;
DROP TABLE dbo.LoginDetails;
Run Code Online (Sandbox Code Playgroud)

或者,如果您有 SQL Server 2016 或更高版本,您可以使用行级安全谓词来防止某些用户看到具有 NULLLastLoggedInAt值的行。行级安全性的 Microsoft Docs在这里

首先,我们创建表、登录名、该登录名的用户,并授予对该表的访问权限:

CREATE TABLE dbo.LoginDetails
(
    Username nvarchar(100) NOT NULL
    , EmailAddress nvarchar(256) NOT NULL
    , LastLoggedInAt datetime NULL
);
GO

CREATE LOGIN RemoteUser 
WITH PASSWORD = '2q1345lkjsadfgsa0(*';

CREATE USER RemoteUser
FOR LOGIN RemoteUser
WITH DEFAULT_SCHEMA = dbo;

GRANT SELECT ON dbo.LoginDetails TO RemoteUser;
Run Code Online (Sandbox Code Playgroud)

接下来,我们插入几个示例行。一行具有 null LastLoggedInAt,另一行具有该列的非空值。

INSERT INTO dbo.LoginDetails(Username, EmailAddress, LastLoggedInAt)
VALUES ('user x', 'x@y.com', NULL)
    , ('user y', 'y@y.com', GETDATE());
Run Code Online (Sandbox Code Playgroud)

在这里,我们创建了一个模式绑定的表值函数,它根据传递给函数的@LastLoggedInAt@username变量的值返回带有 0 或 1 的行。过滤谓词将使用此函数来消除我们想要对某些用户隐藏的行。

CREATE FUNCTION dbo.fn_LoginDetailsRemoteUserPredicate
(
    @LastLoggedInAt datetime
    , @username sysname
)  
RETURNS TABLE  
WITH SCHEMABINDING  
AS  
    RETURN SELECT 1 AS fn_securitypredicate_result   
    WHERE (@username = N'RemoteUser' AND @LastLoggedInAt IS NOT NULL)
        OR @username <> N'RemoteUser';  
GO
Run Code Online (Sandbox Code Playgroud)

这是从SELECT针对dbo.LoginDetails表运行的语句中消除行的安全过滤器:

CREATE SECURITY POLICY LoginDetailsRemoteUserPolicy
ADD FILTER PREDICATE dbo.fn_LoginDetailsRemoteUserPredicate(LastLoggedInAt, USER_NAME())
ON dbo.LoginDetails
WITH (STATE=ON);
Run Code Online (Sandbox Code Playgroud)

上面的过滤器dbo.fn_LoginDetailsRemoteUserPredicate通过传入当前用户的名称以及表中LastLoggedInAt列的每一行的值来使用该函数dbo.LoginDetails

如果我们以普通用户的身份查询表:

SELECT *
FROM dbo.LoginDetails
Run Code Online (Sandbox Code Playgroud)

我们看到所有行:

?????????????????????????????????????????????????????? ???
? 用户名 ?电子邮件地址 ?LastLoggedInAt ?
?????????????????????????????????????????????????????? ???
? 用户 x ? x@y.com ? 空值 ?
? 用户 ? y@y.com ?2018-02-15 13:53:42.577 ?
?????????????????????????????????????????????????????? ???

但是,如果我们测试为RemoteUser

EXECUTE AS LOGIN = 'RemoteUser';

SELECT *
FROM dbo.LoginDetails

REVERT
Run Code Online (Sandbox Code Playgroud)

我们只看到“有效”行:

?????????????????????????????????????????????????????? ???
? 用户名 ?电子邮件地址 ?LastLoggedInAt ?
?????????????????????????????????????????????????????? ???
? 用户 ? y@y.com ?2018-02-15 13:42:02.023 ?
?????????????????????????????????????????????????????? ???

而且,我们清理:

DROP SECURITY POLICY LoginDetailsRemoteUserPolicy;
DROP FUNCTION dbo.fn_LoginDetailsRemoteUserPredicate;
DROP USER RemoteUser;
DROP LOGIN RemoteUser;
DROP TABLE dbo.LoginDetails;
Run Code Online (Sandbox Code Playgroud)

请注意,以这种方式将函数模式绑定到表确实无法在不首先删除过滤谓词和dbo.fn_LoginDetailsRemoteUserPredicate函数的情况下修改表的定义。