有权执行存储过程,但不能直接读取底层表

DWD*_*DWD 9 security stored-procedures permissions sql-server-2008-r2

我有一个存储过程,其中包含来自许多不同表的许多不同列;我想让 20 个用户通过 Excel 访问存储过程的结果。

是否可以让用户只能访问存储过程,而不能访问基础表和视图?

Aar*_*and 14

当然,您只是授予这些用户对存储过程的权限,而不是对基础表的权限。

CREATE PROCEDURE dbo.test -- always use schema prefix!
AS
BEGIN
  SET NOCOUNT ON;

  SELECT t1.col1, t2.col2, ...
    FROM dbo.table1 AS t1
    INNER JOIN dbo.table2 AS t2
    ON ... ;
END
GO

GRANT EXEC ON dbo.test TO [user1], [user2], ...;
Run Code Online (Sandbox Code Playgroud)

这是您可以测试的完全包含的示例。首先,创建一个数据库(希望floob不会与您已有的任何数据库冲突):

CREATE DATABASE floob;
GO
Run Code Online (Sandbox Code Playgroud)

现在,创建两个登录:

CREATE LOGIN floob1 WITH PASSWORD = 'foo', CHECK_POLICY = OFF;
CREATE LOGIN floob2 WITH PASSWORD = 'foo', CHECK_POLICY = OFF;
GO
Run Code Online (Sandbox Code Playgroud)

并创建数据库用户以与他们一起使用:

USE floob;
GO
CREATE USER floob1 FROM LOGIN floob1;
CREATE USER floob2 FROM LOGIN floob2;
GO
Run Code Online (Sandbox Code Playgroud)

现在,创建一个默认情况下他们无权访问的表,以及一个查询它的存储过程:

CREATE TABLE dbo.whatwhat(id INT);
INSERT dbo.whatwhat(id) VALUES(1);
GO
CREATE PROCEDURE dbo.select_whatwhat
AS
BEGIN
  SET NOCOUNT ON;

  SELECT id FROM dbo.whatwhat;
END
GO
Run Code Online (Sandbox Code Playgroud)

现在,floob1仅授予执行权限:

GRANT EXEC ON dbo.select_whatwhat TO floob1;
GO
Run Code Online (Sandbox Code Playgroud)

现在,尝试执行该过程并从表中选择floob1

EXECUTE AS USER = 'floob1';
GO
EXEC dbo.select_whatwhat; -- works
GO
SELECT id FROM dbo.whatwhat; -- fails with error
GO
REVERT;
Run Code Online (Sandbox Code Playgroud)

您通过执行存储过程获得一个成功的查询,但是在尝试直接查询表时出现错误消息,因为您没有被明确授予这样做的访问权限。

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

现在,尝试 as floob2,谁还没有被授予执行存储过程的显式访问权限,要么:

EXECUTE AS USER = 'floob2';
GO
EXEC dbo.select_whatwhat; -- fails with error
GO
SELECT id FROM dbo.whatwhat; -- fails with error
GO
REVERT;
Run Code Online (Sandbox Code Playgroud)

这次您在 上遇到相同的错误SELECT,但在 上也会出现此错误EXEC

消息 229,级别 14,状态 5,过程 select_whatwhat
对象“select_what”、数据库“floob”、架构“dbo”的 EXECUTE 权限被拒绝。

不要忘记清理:

USE master;
GO
DROP DATABASE floob;
DROP LOGIN floob1;
DROP LOGIN floob2;
GO
Run Code Online (Sandbox Code Playgroud)

关于你的评论:

是否可以通过在 Sql 服务器中使用 drop 和 click 而不是使用 T-SQL 代码来授予用户?

是的,但这听起来不是一个好主意。过程如下:您在对象资源管理器中右键单击存储过程,点击属性,移动到权限选项卡,点击搜索...,输入您的用户名,点击确定,然后选中左侧 EXECUTE 与 GRANT 上的复选框顶部,然后单击确定。现在对每个存储过程重复,然后乘以 20(所有用户)。是的,这听起来比编写脚本容易得多。这是一种在一步中生成您需要的脚本的更好方法,只需一次复制、粘贴和执行,而无需知道您需要通过 GUI 以交互方式执行此操作的点数和点击次数:

DECLARE @xml NVARCHAR(MAX) = N'';

;WITH users AS 
(SELECT name = 'user1' UNION ALL SELECT 'user2' ... UNION ALL SELECT 'user20')
SELECT @xml += N'GRANT EXEC ON ' + QUOTENAME(s.name) + 
  '.' + QUOTENAME(p.name) + ' TO ' + QUOTENAME(u.name) + ';
'
FROM users AS u CROSS JOIN sys.procedures AS p
  INNER JOIN sys.schemas AS s ON s.[schema_id] = p.[schema_id];

PRINT @sql;
-- EXEC sp_executesql @sql;
Run Code Online (Sandbox Code Playgroud)

您还可以添加sys.database_principals到组合中,以确保您获得正确的用户组,没有错别字。