具有托管标识(用户分配)的 Azure SQL 登录失败

时间:2021-06-15 20:06:03

标签: c# .net azure azure-managed-identity

我使用的是 .NET Framework(不是 Core!)4.7.2 并且我正在尝试使用用户分配的托管标识来连接到 Azure SQL Server。托管标识对象已添加到 Azure SQL 服务器上的 db_datareaderdb_datawriter 角色,并且具有连接权限。 (通过我们的 ASP.NET Core 应用独立验证)。

但是,当我们尝试从已在托管标识上启用的 VM 上运行的命令行 .NET Framework 应用程序执行此操作时,它会失败。连接到 Azure SQL 的代码大致如下所示:

// .NET Framework 4.7.2
using(SqlConnection connection = new SqlConnection("Server=tcp:myserver.database.windows.net,1433;Database=MyDb;")){
    var credential = new ManagedIdentityCredential(ConfigurationManager.AppSettings["ManagedIdentityId"]);
    conn.AccessToken = await credential.GetTokenAsync(
                          new TokenRequestContext(new[] { "https://database.windows.net/.default" })
                       );
    ....
}

它设法很好地抓取了一个令牌(从下面的日志中),但由于这个错误而失败

System.Data.SqlClient.SqlException (0x80131904): Login failed for user ''

我应该检查什么?

(同样的代码在 ASP.NET Core 中也能正常工作)

附录

  • 与此 issue 非常相似,但在我们的示例中,并非所有形式的身份验证都在 Azure SQL 服务器上失败。我们能够使用 AAD 帐户手动登录。只有从 .NET Framework 4.7.2 调用的用户分配的托管标识失败(它在我们的 ASP.NET Core 程序中运行良好,代码相同)
  • 在此记录
2021-06-15 14:39:28.7200|INFO|Logger|Received TOKEN - [xxxx real token here xxxx]
2021-06-15 14:39:28.9387|ERROR|Logger|Exception occurred: 
   System.Data.SqlClient.SqlException (0x80131904): Login failed for user ''.
   at System.Data.SqlClient.SqlInternalConnectionTds..ctor(DbConnectionPoolIdentity identity, SqlConnectionString connectionOptions, 
      SqlCredential credential, Object providerInfo, String newPassword, SecureString newSecurePassword, Boolean redirectedUserInstance, 
      SqlConnectionString userConnectionOptions, SessionData reconnectSessionData, DbConnectionPool pool, String accessToken, 
      Boolean applyTransientFaultHandling, SqlAuthenticationProviderManager sqlAuthProviderManager)
   at System.Data.SqlClient.SqlConnectionFactory.CreateConnection(DbConnectionOptions options, DbConnectionPoolKey poolKey, 
                                                                  Object poolGroupProviderInfo, DbConnectionPool pool, 
                                                                  DbConnection owningConnection, DbConnectionOptions userOptions)
   at System.Data.ProviderBase.DbConnectionFactory.CreatePooledConnection(DbConnectionPool pool, DbConnection owningObject, 
                                                                          DbConnectionOptions options, DbConnectionPoolKey poolKey, 
                                                                          DbConnectionOptions userOptions)
   at System.Data.ProviderBase.DbConnectionPool.CreateObject(DbConnection owningObject, DbConnectionOptions userOptions, DbConnectionInternal oldConnection)
   at System.Data.ProviderBase.DbConnectionPool.UserCreateRequest(DbConnection owningObject, DbConnectionOptions userOptions, DbConnectionInternal oldConnection)
   at System.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, UInt32 waitForMultipleObjectsTimeout, 
                                                                 Boolean allowCreate, Boolean onlyOneCheckConnection, DbConnectionOptions userOptions, 
                                                                 DbConnectionInternal& connection)
   at System.Data.ProviderBase.DbConnectionPool.WaitForPendingOpen()

1 个答案:

答案 0 :(得分:1)

根据我的测试,对我来说还可以。我的步骤如下。

  1. Assign MSI to the VM

  2. 配置 Azure AD 身份验证

DROP USER IF EXISTS [the msi name]
GO
CREATE USER [the msi name] FROM EXTERNAL PROVIDER;
GO
ALTER ROLE db_datareader ADD MEMBER [the msi name];
ALTER ROLE db_datawriter ADD MEMBER [the msi name];
GRANT EXECUTE TO [the msi name]
GO
  1. 安装包
Install-Package Azure.Identity -Version 1.4.0
Install-Package System.Data.SqlClient -Version 4.8.2
  1. 代码
static async Task Main(string[] args)
        {
            string conStr = "Server=tcp:testsql09.database.windows.net,1433;Database=test;";
            using (SqlConnection connection = new SqlConnection(conStr))
            {
                var credential = new ManagedIdentityCredential("ed5f***823");
                connection.AccessToken = (await credential.GetTokenAsync(
                              new TokenRequestContext(new[] { "https://database.windows.net//.default" })
                    )).Token;
                String sql = "SELECT * FROM Blogs";

                using (var sqlCommand = new SqlCommand(sql, connection))
                {
                    connection.Open();
                    using (SqlDataReader reader = sqlCommand.ExecuteReader())
                    {
                        while (reader.Read())
                        {
                            Console.WriteLine("{0} {1}", reader.GetInt32(0), reader.GetString(1));
                        }
                    }
                }

            }
            Console.Read();
        }

enter image description here