无法使用 EXECUTE AS OWNER 在存储过程中创建登录名?

Cannot create login in stored procedure with EXECUTE AS OWNER?

运行SQL Server 2014 Express。登录为 sa 我尝试执行下面的代码。它给了我以下错误:

Msg 15247, Level 16, State 1, Line 25 User does not have permission to perform this action.

为什么?!如果我在selectSYSTEM_USER程序里面,确实是sa(正版)。

USE [MyDatabase]
GO

CREATE PROCEDURE [dbo].[create_login]
    @Login [nvarchar](256),
    @Password [nvarchar](128)
WITH EXECUTE AS OWNER
AS
BEGIN
    SET NOCOUNT ON

    DECLARE @Sql NVARCHAR(4000)

    SET @sql = N'CREATE LOGIN ' + QUOTENAME(@Login) + N' WITH PASSWORD = '
        + QUOTENAME(@Password, N'''') + N',CHECK_EXPIRATION=OFF, CHECK_POLICY=ON;'
    EXEC (@sql)
END
GO

GRANT EXECUTE ON [dbo].[create_login] TO [my_simple_role]
GO

-- Let's go!
EXEC [dbo].[create_login] N'NewUser', N'c0Mpl3xP@55w0rd'
GO

-- Error! :(

如果我 运行 语句 存储过程之外,它会起作用。

我正在尝试制作一个允许普通用户添加服务器登录的存储过程。以上似乎不起作用。请指教!

登录名是服务器级对象,因此需要服务器级权限。 EXECUTE AS <database-user> 是数据库范围的安全上下文。

通过存储过程授予普通用户特权操作的一种方法是使用映射到具有必要权限的登录名的证书对模块进行签名。这种情况下的必要步骤是:

  1. 在主数据库中创建证书
  2. 为该证书创建一个登录名
  3. 授予证书登录权限以创建登录名
  4. 从 master 导出证书
  5. 将证书导入应用程序数据库
  6. 使用证书签署存储过程

下面是从 Erland Sommarskog's web site 中收集的示例。请注意,每次更改时,您都需要使用证书重新签署 proc。

--create database master key, if necessary
IF NOT EXISTS(SELECT 1 FROM sys.symmetric_keys WHERE name = N'##MS_DatabaseMasterKey##')
BEGIN
    CREATE MASTER KEY ENCRYPTION BY PASSWORD='M@sterkEEPassw0rd';
END;
GO

CREATE CERTIFICATE SecurityAdministratorCertificate
   WITH
     SUBJECT = 'Allows non-privileged users to create and alter logins'
   , START_DATE = '20020101'
   , EXPIRY_DATE = '20300101';
GO

CREATE LOGIN SecurityAdministratorCertificateLogin
    FROM CERTIFICATE SecurityAdministratorCertificate;
GO
GRANT ALTER ANY LOGIN TO SecurityAdministratorCertificateLogin;
GO

--export cert from master
DECLARE @CERTENC VARBINARY(MAX);
DECLARE @CERTPVK VARBINARY(MAX);
SELECT @CERTENC = CERTENCODED(CERT_ID(N'SecurityAdministratorCertificate'));
SELECT @CERTPVK = CERTPRIVATEKEY(CERT_ID(N'SecurityAdministratorCertificate'),
       'All you need is love');

DECLARE @sql nvarchar(MAX);
SELECT @sql = N'CREATE CERTIFICATE SecurityAdministratorCertificate FROM BINARY = '
    + CONVERT(nvarchar(MAX), @CERTENC, 1)
    + ' WITH PRIVATE KEY ( BINARY = '
    + CONVERT(nvarchar(MAX), @CERTPVK, 1)
    + ', DECRYPTION BY PASSWORD = ''All you need is love'');'

--import cert into app databases
USE MyDatabase;

--create database master key, if necessary
IF NOT EXISTS(SELECT 1 FROM sys.symmetric_keys WHERE name = N'##MS_DatabaseMasterKey##')
BEGIN
    CREATE MASTER KEY ENCRYPTION BY PASSWORD='M@sterkEEPassw0rd';
END;

EXEC(@sql);
GO

CREATE PROCEDURE [dbo].[create_login]
    @Login [nvarchar](256),
    @Password [nvarchar](128)
AS
BEGIN
    SET NOCOUNT ON;

    DECLARE @Sql NVARCHAR(4000);

    SET @sql = N'CREATE LOGIN ' + QUOTENAME(@Login) + N' WITH PASSWORD = '
        + QUOTENAME(@Password, N'''') + N',CHECK_EXPIRATION=OFF, CHECK_POLICY=ON;';

    EXEC (@sql);

END
GO

--grant exec permissions to users
GRANT EXECUTE ON [dbo].[create_login] TO [my_simple_role];
GO 

--sign proc with certificate
ADD SIGNATURE TO dbo.create_login BY CERTIFICATE SecurityAdministratorCertificate;
GO

编辑:

以上示例使用数据库主密钥而不是证书专用密码对证书私钥进行加密。在数据库恢复或附加到不同的 SQL 实例的情况下(不恢复主数据库),您需要重新创建服务器级别 master 数据库中的对象,包括应用程序所需的所有登录名和存储在 master 中的证书。要恢复证书,一种方法是在 restore/attach 之后将证书从用户数据库复制到 master,然后重新创建具有权限的证书登录。在这种情况下无法自动打开 DMK,因为加密数据库主密钥的服务主密钥在新实例上是不同的。需要原始密码才能在将证书复制到主数据库的脚本中手动打开 DMK。用于在数据库之间复制证书的证书密码是临时的,不需要保留。

这是在还原或附加后在主服务器中重新创建证书和证书登录的示例:

USE MyDatabase;
--open DMK with original password
OPEN MASTER KEY DECRYPTION BY PASSWORD='M@sterkEEPassw0rd';
--export cert from user database
USE MyDatabase;
DECLARE @CERTENC VARBINARY(MAX);
DECLARE @CERTPVK VARBINARY(MAX);
SELECT @CERTENC = CERTENCODED(CERT_ID(N'SecurityAdministratorCertificate'));
SELECT @CERTPVK = CERTPRIVATEKEY(CERT_ID(N'SecurityAdministratorCertificate'),
       'temporary password here');

DECLARE @sql nvarchar(MAX);
SELECT @sql = N'CREATE CERTIFICATE SecurityAdministratorCertificate FROM BINARY = '
    + CONVERT(nvarchar(MAX), @CERTENC, 1)
    + ' WITH PRIVATE KEY ( BINARY = '
    + CONVERT(nvarchar(MAX), @CERTPVK, 1)
    + ', DECRYPTION BY PASSWORD = ''temporary password here'');'
SELECT @sql
CLOSE MASTER KEY;

--import cert into master
USE master;

--create database master key in new instance master database, if necessary
IF NOT EXISTS(SELECT 1 FROM sys.symmetric_keys WHERE name = N'##MS_DatabaseMasterKey##')
BEGIN
    CREATE MASTER KEY ENCRYPTION BY PASSWORD='M@sterkEEPassw0rd';
END;

EXEC(@sql);
GO

--recreate login and assign permissions
CREATE LOGIN SecurityAdministratorCertificateLogin
    FROM CERTIFICATE SecurityAdministratorCertificate;
GRANT ALTER ANY LOGIN TO SecurityAdministratorCertificateLogin;
GO