选择不同的用户在查询中工作,但不在存储过程中

时间:2017-11-23 07:54:19

标签: sql-server stored-procedures permissions schema sql-server-2016

环境:SQL SERVER 2016 我在SQL中使用execute as user函数来创建特定于会话的表而不使用动态SQL。当我将代码作为单个查询运行时,它的执行完全符合预期。工作代码是:

    DECLARE @strSQL NVARCHAR(MAX)
    , @strSession AS NVARCHAR(256) 
    SET @strSession = 'SESSION_ABCD'

    SET @strSQL = 'CREATE SCHEMA ' + @strSession
    PRINT (@strSQL) 
    EXEC (@strSQL)

    SET @strSQL = 'CREATE LOGIN ' + @strSession + ' WITH PASSWORD = ''' + CAST(NEWID() AS NVARCHAR(36)) + ''' '
    PRINT (@strSQL) 
    EXEC (@strSQL)

    SET @strSQL = 'CREATE USER ' + @strSession
    PRINT (@strSQL) 
    EXEC (@strSQL)

    SET @strSQL = 'ALTER USER ' + @strSession + ' WITH DEFAULT_SCHEMA = ' + @strSession
    PRINT (@strSQL) 
    EXEC (@strSQL)

    SET @strSQL = 'GRANT ALTER ON SCHEMA::' + @strSession + ' TO ' + @strSession
    PRINT (@strSQL) 
    EXEC (@strSQL)      

    SET @strSQL = 'GRANT CREATE TABLE TO ' + @strSession
    PRINT (@strSQL) 
    EXEC (@strSQL)

    SET @strSQL = 'GRANT SELECT,INSERT,UPDATE,DELETE TO ' + @strSession
    PRINT (@strSQL) 
    EXEC (@strSQL)

    EXECUTE AS USER = 'SESSION_ABCD'

    -- Create table without including default schema - OK
    -- Creates table SESSION_ABCD.Test as expected
    CREATE TABLE Test (MyValue INT)

    -- Select from table including schema name - OK         
    SELECT  *
    FROM    SESSION_ABCD.Test

    -- Select from table excluding schema name - Works outside of stored procedure
    SELECT  *
    FROM    Test

    REVERT

但是,如果我在存储的[程序中运行相同的代码,我会得到意想不到的结果。代码是:

CREATE PROC dbo.SetupUser
AS
BEGIN
    DECLARE @strSession AS NVARCHAR(256) 
    SET @strSession = 'SESSION_ABCD'

    DECLARE @strSQL NVARCHAR(MAX)

    SET @strSQL = 'CREATE SCHEMA ' + @strSession
    PRINT (@strSQL) 
    EXEC (@strSQL)

    SET @strSQL = 'CREATE LOGIN ' + @strSession + ' WITH PASSWORD = ''' + CAST(NEWID() AS NVARCHAR(36)) + ''' '
    PRINT (@strSQL) 
    EXEC (@strSQL)

    SET @strSQL = 'CREATE USER ' + @strSession
    PRINT (@strSQL) 
    EXEC (@strSQL)

    SET @strSQL = 'ALTER USER ' + @strSession + ' WITH DEFAULT_SCHEMA = ' + @strSession
    PRINT (@strSQL) 
    EXEC (@strSQL)

    SET @strSQL = 'GRANT ALTER ON SCHEMA::' + @strSession + ' TO ' + @strSession
    PRINT (@strSQL) 
    EXEC (@strSQL)      

    SET @strSQL = 'GRANT CREATE TABLE TO ' + @strSession
    PRINT (@strSQL) 
    EXEC (@strSQL)

    SET @strSQL = 'GRANT SELECT,INSERT,UPDATE,DELETE TO ' + @strSession
    PRINT (@strSQL) 
    EXEC (@strSQL)
END

GO

CREATE PROC dbo.SetupTable
AS
BEGIN

    EXECUTE AS USER = 'SESSION_ABCD'

    -- Create table without including default schema - OK
    -- Creates table SESSION_ABCD.Test as expected
    CREATE TABLE Test (MyValue INT)

    -- Select from table including schema name - OK         
    SELECT  *
    FROM    SESSION_ABCD.Test

    -- Select from table excluding schema name - Fails
    -- Expecting same result as above

    PRINT SCHEMA_NAME()
    SELECT  *
    FROM    Test

    REVERT
END

GO

EXEC dbo.SetupUser

EXEC dbo.SetupTable

当我执行上面的脚本时,users / sessions / etc正确配置。 (请注意,您需要从系统中删除它们以再次运行它们。)在SetupTable过程中,create table命令针对默认模式执行,但是select命令需要使用模式名称进行限定。虽然我可以使用Dynamic SQL构建查询以插入模式名称,但此项目完全是删除旧的动态sql代码。

我会诚实地说这不是我之前使用的方法,但似乎是解决另一个问题的最佳解决方案。我希望有一些我缺少的基本内容,但此刻我完全被难倒了。

非常感谢。

1 个答案:

答案 0 :(得分:1)

您的第二个过程是在dbo架构中创建的,如果您未在其中使用完全限定名称,则默认架构为存储过程架构dbo case),而不是用户的默认架构。

例如,您的用户A包含默认架构a,架构B,和架构dbo

您创建了一个proc B.sp_test:

create proc B.sp_test as select * from test_tbl

现在用户A执行此sp。

test_tbl不是模式限定的,因此服务器首先查找B.test_tbl。 如果表存在,则从中进行选择。如果不是,则查找dbo.test_tbl。 如果找不到该表,则表示您输入了错误。

这不是用户的已验证的默认架构,但它是过程的架构,然后验证了dbo架构。

这与表创建不同。 当用户创建表而不使用模式对其进行限定时,将使用它的默认scema。这与创建表的位置无关,在sp内或不在。

如何向自己证明?

只需在不同的架构中创建一个过程。 创建3个具有相同名称test_tbl的表,将它们放在默认模式,dbo和sp模式中。 在每个表中插入它的模式名称。

在sp中从test_tbl创建一个SELECT,在所有3个表存在时运行它。 您将从sp的架构中的表中获取数据。

现在删除此表,再次运行sp。

这次你将从dbo.test_tbl获取数据。

最后,删除dbo.test_tbl,运行sp。这次你会收到错误。