2017-06-13 110 views
1

在每次DAO测试之前,我清理我的数据库,并且需要重置某些表的标识值。我创建了以下存储过程:如何启用非系统管理员帐户来执行“DBCC CHECKIDENT”?

CREATE PROCEDURE SET_IDENTITY 
    @pTableName   varchar(120), 
    @pSeedValue   int 
AS 
BEGIN 
    DBCC CHECKIDENT(@pTableName, RESEED, @pSeedValue); 
END 

我的问题是我需要用“普通”用户调用此存储过程。按顺序工作,该用户不能成为:sysadmin,db_owner,db_ddladmin的成员。

我已经试过:

一)CREATE PROCEDURE WITH EXECUTE AS OWNER

B)EXECUTE AS USER = 'sa' before call DBCC CHECKIDENT

但在这两种情况下,我回来了:

服务器主体sa无法访问在当前安全上下文下的数据库my_db_name

我使用微软的SQL Server Express(64位)11.0.2100.60

谢谢你在前进,

阿贝尔

+0

如果您的环境允许,请尝试使用'ALTER AUTHORIZATION ON DATABASE :: [mydb] TO [sa]'将数据库所有者设置为SQL Server可以验证的内容(确保您的数据库没有组帐户首先由设计师设计一个所有者!)然后'EXECUTE AS OWNER'应该可以工作。对于更强大的解决方案,您可以使用加密签名为存储过程提供必要的许可,但[涉及更多](http://sommarskog.se/grantperm.html#Certificates)。 –

+0

另一种可能的解决方案是,如果“clean”意味着“清除整个表”,则使用'TRUNCATE TABLE',作为副作用也会重置身份并且只需要表中的'ALTER'权限。还要考虑使用[数据库项目](https://msdn.microsoft.com/library/hh272677)来创建一个干净的空数据库,您可以在每次测试运行之前部署(或者每次测试,甚至是,但这可能太慢)。另一种选择是恢复到[快照](https://docs.microsoft.com/zh-cn/sql/relational-databases/databases/database-snapshots-sql-server)或分离/附加空的数据库。 –

+0

规范声明您不能在表上使用TRUNCATE TABLE,而是由FOREIGN KEY约束引用。 –

回答

0

来电有自己的模式或者是的db_owner

从DOC:DBCC CHECKIDENT

个权限

来电必须拥有包含表的模式,或者是系统管理员固定服务器角色的成员,该的db_owner固定数据库角色,或者db_ddladmin固定数据库角色。

LiveDemo

+0

@AbelaneIROS您是否尝试过CREATE PROCEDURE WITH EXECUTE AS'user_name'' 'user_name'拥有模式还是db_owner? – lad2025

0

这是很容易做到,但总的来讲,你不应该需要每次重置标识值。具体的身份值应该不重要,所以唯一的担心应该是可能由于重复测试而达到最大值。在这种情况下,我不建议每次都重新设置,因为让ID达到较高值也是一个很好的测试,因此您可以确保所有代码路径正确处理它们,并找到不在用户使用之前的区域;-) 。这就是说,你需要做的就是创建一个非对称密钥,然后从中创建一个用户,然后将该用户添加到​​固定的数据库角色,并最终签名使用相同非对称密钥的存储过程。

下面的例子说明了这种行为:

USE [tempdb]; 

CREATE TABLE dbo.CheckIdent 
(
    [ID] INT NOT NULL IDENTITY(1, 1) CONSTRAINT [PK_CheckIdentity] PRIMARY KEY, 
    [Something] VARCHAR(50) 
); 

EXEC(N' 
CREATE PROCEDURE dbo.SET_IDENTITY 
    @pTableName   sysname, 
    @pSeedValue   int 
AS 
BEGIN 
    DBCC CHECKIDENT(@pTableName, RESEED, @pSeedValue); 
END; 
'); 

CREATE USER [MrNobody] WITHOUT LOGIN; 

GRANT EXECUTE ON dbo.SET_IDENTITY TO [MrNobody]; 

------- 

EXECUTE AS USER = N'MrNobody'; 
SELECT SESSION_USER AS [CurrentUser], ORIGINAL_LOGIN() AS [OriginalLogin]; 

EXEC dbo.SET_IDENTITY N'dbo.CheckIdent', 12; 
/* 
Msg 2557, Level 14, State 5, Procedure SET_IDENTITY, Line 7 [Batch Start Line 30] 
User 'MrNobody' does not have permission to run DBCC CHECKIDENT for object 'CheckIdent'. 
*/ 

REVERT; 
SELECT SESSION_USER AS [CurrentUser], ORIGINAL_LOGIN() AS [OriginalLogin]; 

------- 

CREATE ASYMMETRIC KEY [DdlAdminPermissionsKey] 
    WITH ALGORITHM = RSA_2048 
    ENCRYPTION BY PASSWORD = 'not_a_good_password'; 

CREATE USER [DdlAdminPermissions] 
    FROM ASYMMETRIC KEY [DdlAdminPermissionsKey]; 

ALTER ROLE [db_ddladmin] ADD MEMBER [DdlAdminPermissions]; 

ADD SIGNATURE 
    TO dbo.SET_IDENTITY 
    BY ASYMMETRIC KEY [DdlAdminPermissionsKey] 
    WITH PASSWORD = 'not_a_good_password'; 

------- 

EXECUTE AS USER = N'MrNobody'; 
SELECT SESSION_USER AS [CurrentUser], ORIGINAL_LOGIN() AS [OriginalLogin]; 

EXEC dbo.SET_IDENTITY N'dbo.CheckIdent', 12; 
-- Success! 

REVERT; 
SELECT SESSION_USER AS [CurrentUser], ORIGINAL_LOGIN() AS [OriginalLogin]; 

其他小的笔记:

  1. 您不能执行作为用户= sa因为sa是登录(服务器级别),而不是用户(数据库水平)。您可以使用EXECUTE AS LOGIN = 'sa';,但那需要IMPERSONATE权限,并且是一个安全漏洞,因为非特权登录可以随时运行EXECUTE AS LOGIN = 'sa'。所以不要这样做。
  2. 对于包含SQL Server对象名称,索引等的变量/参数,应该使用NVARCHAR而不是VARCHAR。大多数内部名称使用sysname,这是NVARCHAR(128)的系统别名,所以sysname通常是首选数据类型。
相关问题