【问题标题】:The server principal is not able to access the database under the current security context in SQL Server MS 2012服务器主体无法访问 SQL Server MS 2012 中当前安全上下文下的数据库
【发布时间】:2013-10-01 07:33:17
【问题描述】:

我正在尝试通过 SQL Server Management Studio 访问我的托管服务器的数据库,在登录之前一切正常,但是当我使用命令 use myDatabase 时,它给了我这个错误:

The server principal "****" is not able to access the database "****" under the current security context.

我搜索了一遍,托管服务提供商列出了this 解决问题。

但这对我不起作用,可能是因为它适用于 SQL Server Management Studio 2008,但我使用的是 SQL Server Management Studio 2012。

这可能是个问题吗?如果是的话,谁能告诉我它在 SSMS 2012 中的替代方案?

【问题讨论】:

  • '托管服务提供商'?我们是在谈论专用的还是共享的?如果它是共享托管服务器,我强烈建议您联系您的托管服务提供商寻求帮助。共享主机环境中的 SQL 是出了名的错误和问题。它与产品无关,而是托管服务提供商应用于服务器的策略。每个托管公司都有自己的方式来利用 SQL,或者看起来如此。

标签: sql-server sql-server-2012


【解决方案1】:

我遇到了一个特定于 USER WITHOUT LOGIN 的问题,在将数据库备份并还原到另一台服务器后,用户失去了与数据库的连接。

为了解决这个问题,我们需要确保用户已连接到数据库。

GRANT CONNECT TO [DatabaseUser]
GO

这类似于上面 Salim Gangji 给出的答案,但特定于 USER WITHOUT LOGIN 的情况。

【讨论】:

    【解决方案2】:

    SQL Logins 是在服务器级别定义的,必须映射到特定数据库中的Users

    在 SSMS 对象资源管理器中,在您要修改的服务器下,展开 Security > Logins,然后双击相应的登录条目。这将打开“登录属性”对话框。

    选择用户映射,这将显示服务器上的所有数据库。那些已经将用户映射到该登录名的用户将选中“映射”复选框。从这里您可以选择其他数据库(并确保在每个数据库中选择用户应属于的角色),然后单击确定添加映射。

    请注意,虽然通常将用户命名为与登录名相同以避免混淆,但它们不必匹配,您可以随意命名用户。

    在还原或类似操作后,这些映射可能会断开连接。在这种情况下,用户可能仍然存在于数据库中,但实际上并未映射到登录名。如果发生这种情况,您可以运行以下命令来恢复登录:

    USE {database};
    ALTER USER {user} WITH login = {login}
    

    您也可以删除数据库用户并从“登录属性”对话框重新创建它,但任何角色成员资格或其他设置都需要重新创建。

    【讨论】:

    • 您的评论应该是公认的答案。它清楚地描述了: - SQL 登录位于 服务器级别 - SQL 用户位于 Db 级别 - 将登录映射到用户 缺少操作项
    • 很好的答案。这正是我所需要的。谢谢!
    【解决方案3】:

    在 SQL 2017 上 - 数据库 A 具有数据库 B 的同义词。用户可以连接到数据库 A,并且对指向 B 的同义词的 sp(在 A 上)拥有执行权限。用户设置为连接访问 B . 只有当向数据库 B 的公共组授予 CONNECT 时,A 上的 sp 才起作用。我不记得在 2012 年以这种方式工作,因为仅授予用户连接似乎有效。

    【讨论】:

      【解决方案4】:

      我相信您在创建数据库用户时可能缺少“Grant Connect To”语句。

      下面是完整的 sn-p,您需要创建针对 SQL Server DBMS 的登录以及针对数据库的用户

      USE [master]
      GO
      
      CREATE LOGIN [SqlServerLogin] WITH PASSWORD=N'Passwordxyz', DEFAULT_DATABASE=[master], CHECK_EXPIRATION=OFF, CHECK_POLICY=ON
      GO
      
      USE [myDatabase]
      GO
      
      CREATE USER [DatabaseUser] FOR LOGIN [SqlServerLogin] WITH DEFAULT_SCHEMA=[mySchema]
      GO
      
      GRANT CONNECT TO [DatabaseUser]
      GO
      
      -- the role membership below will allow you to run a test "select" query against the tables in your database
      ALTER ROLE [db_datareader] ADD MEMBER [DatabaseUser]
      GO
      

      【讨论】:

        【解决方案5】:

        这对我有用:

        use <Database>
        EXEC  sp_change_users_login @Action='update_one', @UserNamePattern='<userLogin>',@LoginName='<userLogin>';
        

        问题可以通过以下方式可视化:

        SELECT sid FROM sys.sysusers WHERE name = '<userLogin>'
        SELECT sid FROM sys.syslogins WHERE name = '<userLogin>';
        

        【讨论】:

        • 这为我解决了问题。谢谢 ! “这个问题可以用“可视化” -> 如果它们返回不同的哈希,则存在问题,上面的查询将同步它们。
        【解决方案6】:

        我花了很长时间来解决这个问题,然后我意识到我犯了一个简单的错误,因为我忘记了我的连接目标是哪个特定数据库。我使用标准 SQL Server 连接窗口来输入凭据:

        我必须检查 连接属性 选项卡以确认我选择了正确的数据库进行连接。我不小心将此处的 连接到数据库 选项设置为上一个会话的选择。这就是为什么我无法连接到我认为我正在尝试连接的数据库。

        请注意,您需要单击Options &gt;&gt; 按钮才能显示连接属性和其他选项卡。

        【讨论】:

        • 这对我有用,因为我登录的用户只能访问特定的数据库。谢谢!
        【解决方案7】:

        即使用户已正确映射到登录名,我们也遇到了同样的错误。

        在尝试删除该用户后,发现一些 SP 包含“with execute as”该用户。

        问题已通过删除这些 SP、删除用户、重新创建链接到登录的用户以及重新创建 SP 来解决。

        它可能是从备份恢复(在相关登录不存在期间)或批量模式同步(如果可以创建一个带有执行的 SP,即使用户不存在)。也可能与this answer 有关。

        【讨论】:

        • 您能否详细说明您所说的 SP 是什么意思?
        • 存储过程。在创建 SP(create proc xxx ...)时,有一个可选子句“with execute as ”指定 SP 将像该用户运行它而不是当前登录的用户一样运行。跨度>
        【解决方案8】:

        我在 vb.net 中使用服务器管理对象 (SMO) 时遇到了同样的错误(我确定在 C# 中也是如此)

        Techie Joe 对最初帖子的评论是一个有用的警告,即在共享托管中会发生许多其他事情。花了一点时间才弄清楚,但下面的代码显示了访问 SQL 数据库的方式必须非常具体。每当 SMO 调用在共享托管环境中不精确时,似乎就会出现“服务器主体.​​..”错误。

        第一部分代码针对本地 SQL Express 服务器,并依赖于简单的 Windows 身份验证。这些示例中使用的所有代码均基于 Robert Kanasz 在此Code Project website article 中的 SMO 教程:

          Dim conn2 = New ServerConnection()
          conn2.ServerInstance = "<local pc name>\SQLEXPRESS"
          Try
            Dim testConnection As New Server(conn2)
            Debug.WriteLine("Server: " + testConnection.Name)
            Debug.WriteLine("Edition: " + testConnection.Information.Edition)
            Debug.WriteLine(" ")
        
            For Each db2 As Database In testConnection.Databases
              Debug.Write(db2.Name & " - ")
              For Each fg As FileGroup In db2.FileGroups
                Debug.Write(fg.Name & " - ")
                For Each df As DataFile In fg.Files
                  Debug.WriteLine(df.Name + " - " + df.FileName)
                Next
              Next
            Next
            conn2.Disconnect()
        
          Catch err As Exception
            Debug.WriteLine(err.Message)
          End Try
        

        上面的代码可以很好地找到本地 SQLEXPRESS 服务器上每个数据库的 .mdf 文件,因为身份验证是由 Windows 处理的,而且它在所有数据库中都很广泛。

        在以下代码中,有 2 个部分对 .mdf 文件进行迭代。在这种情况下,只有寻找文件组的第一次迭代有效,并且它只找到一个文件,因为连接只连接到共享主机环境中的一个数据库。

        第二次迭代是上述迭代的副本,它立即阻塞,因为它的编写方式试图访问共享环境中的第一个数据库,而不是用户 ID/密码的数据库应用,因此 SQL 服务器以“服务器主体.​​..”错误的形式返回授权错误。

        Dim sqlConnection1 As New System.Data.SqlClient.SqlConnection
        sqlConnection1.ConnectionString = "connection string with User ID/Password to a specific database in a shared hosting system. This string will likely also include the Data Source and Initial Catalog parameters"
        Dim conn1 As New ServerConnection(sqlConnection1)
        Try
          Dim testConnection As New Server(conn1)
          Debug.WriteLine("Server: " + testConnection.Name)
          Debug.WriteLine("Edition: " + testConnection.Information.Edition)
          Debug.WriteLine(" ")
        
          Dim db2 = testConnection.Databases("the name of the database to which the User ID/Password in the connection string applies")
          For Each fg As FileGroup In db2.FileGroups
            Debug.Write(fg.Name & " - ")
            For Each df As DataFile In fg.Files
              Debug.WriteLine(df.Name + " - " + df.FileName)
            Next
          Next
        
          For Each db3 As Database In testConnection.Databases
            Debug.Write(db3.Name & " - ")
            For Each fg As FileGroup In db3.FileGroups
              Debug.Write(fg.Name & " - ")
              For Each df As DataFile In fg.Files
                Debug.WriteLine(df.Name + " - " + df.FileName)
              Next
            Next
          Next
        
          conn1.Disconnect()
        
        Catch err As Exception
          Debug.WriteLine(err.Message)
        End Try
        

        在第二次迭代循环中,代码编译良好,但由于 SMO 未设置为使用精确语法准确访问正确的数据库,因此尝试失败。

        由于我刚刚学习 SMO,我认为其他新手可能会很高兴知道这个错误还有一个更简单的解释 - 我们只是编码错误。

        【讨论】:

          【解决方案9】:

          在我的情况下,该消息是由无意中在“对象名称”中包含数据库名称的同义词引起的。当我以新名称恢复数据库时,同义词仍指向旧数据库名称。由于用户在旧数据库中没有权限,因此出现了该消息。为了解决这个问题,我删除并重新创建了同义词,而不用数据库名称限定对象名称:

              USE [new_db]
          GO
          
          /****** Object:  Synonym [dbo].[synTable]    Script Date: 10/15/2015 9:45:01 AM ******/
          DROP SYNONYM [dbo].[synTable]
          GO
          
          /****** Object:  Synonym [dbo].[synTable]    Script Date: 10/15/2015 9:45:01 AM ******/
          CREATE SYNONYM [dbo].[synTable] FOR [dbo].[tTheRealTable]
          GO
          

          【讨论】:

            【解决方案10】:

            在我们的 PROD 环境中向 SSRS 部署报告时遇到了同样的错误。发现这个问题甚至可以用“use”语句来重现。解决方案是将用户的 GUID 帐户引用与相关数据库重新同步(即,像在恢复数据库后一样使用“sp_change_users_login”)。附上用于重新同步所有帐户的股票(光标驱动)脚本:

            USE <your database>
            GO
            
            -------- Reset SQL user account guids ---------------------
            DECLARE @UserName nvarchar(255) 
            DECLARE orphanuser_cur cursor for 
                  SELECT UserName = su.name 
                  FROM sysusers su
                  JOIN sys.server_principals sp ON sp.name = su.name
                  WHERE issqluser = 1 AND
                        (su.sid IS NOT NULL AND su.sid <> 0x0) AND
                        suser_sname(su.sid) is null 
                  ORDER BY su.name 
            
            OPEN orphanuser_cur 
            FETCH NEXT FROM orphanuser_cur INTO @UserName 
            
            WHILE (@@fetch_status = 0)
            BEGIN 
            --PRINT @UserName + ' user name being resynced' 
            exec sp_change_users_login 'Update_one', @UserName, @UserName 
            FETCH NEXT FROM orphanuser_cur INTO @UserName 
            END 
            
            CLOSE orphanuser_cur 
            DEALLOCATE orphanuser_cur
            

            【讨论】:

            • 为我工作谢谢。我已将具有 SQL 服务器身份验证的数据库复制到我的测试服务器,但无法访问。现在是
            • 如果用户存在于数据库中但未能持久映射到登录名,则通过 SSMS 对象资源管理器删除所述用户然后重新映射登录名对我有用。否则,我怀疑需要采用上面提出的解决方案。
            【解决方案11】:

            检查您的用户是否映射到您尝试登录的数据库。

            【讨论】:

            • @Graham 使用 SQL Server Management Studio 检查用户或查看此答案:stackoverflow.com/a/9356725/804773
            • 我建议寻找触发器,这就是我收到此消息的原因,有一个触发器在我的用户未授权的另一个数据库中执行某些操作。
            • 我遇到了 OP 的错误并拒绝了这个答案,我发现我只是在连接到 Azure SQL 数据库的连接字符串中的数据库名称中有一个愚蠢的错字。如果您的数据库名称正确,则不需要访问 Master。如果它是错误的,那么(在我的情况下)我认为实体框架(6.1.3)试图通过连接到 Master 以获得一些额外的信息来变得更加聪明(尽管这可能与 EF 无关——我不确定)。但我的解决方案是确保我的连接字符串是正确的。对于错误的数据库名称,我预计会出现非常不同的错误。 :-/
            • 要添加到@DanielV 的评论中,还要检查存储过程以获取任何硬编码的数据库名称。在我的情况下修复了它(大约 20 个存储过程必须更改)。
            • @scott 被映射到。
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2010-10-07
            • 1970-01-01
            • 1970-01-01
            • 2018-03-29
            相关资源
            最近更新 更多