代码之家  ›  专栏  ›  技术社区  ›  illremember thistime

.netcore 2.2 API在使用用户分配的标识时无法从aad获取令牌

  •  1
  • illremember thistime  · 技术社区  · 6 年前

    当使用用户分配的托管标识时,我们无法从Azure应用程序服务查询Azure中的SQL数据库(如果使用系统分配的托管标识,它可以正常工作)。

    该应用程序是.NET核心2.2 Web API应用程序。

    我们为一个Azure应用服务设置了一个用户分配的标识。

    此标识已使用以下命令设置为AD SQL管理员:

    az sql server ad admin create--resource group iactests--server iactestsql--object id-u iactestmanagedIdentity

    令牌生成如下:

    services.AddDbContext<SchoolContext>(options => options.UseSqlServer(new 
    SqlConnection
            {
                ConnectionString = configuration.GetConnectionString("SchoolContext"),
                AccessToken = isDevelopmentEnvironment ? null : new AzureServiceTokenProvider().GetAccessTokenAsync("https://database.windows.net/").Result
            }), ServiceLifetime.Scoped);
    

    这是我们得到的错误:

        Microsoft.Azure.Services.AppAuthentication.AzureServiceTokenProviderException: Parameters: Connection String: [No connection string specified], Resource: https://database.windows.net/, Authority: . Exception Message: Tried the following 3 methods to get an access token, but none of them worked.
    Parameters: Connection String: [No connection string specified], Resource: https://database.windows.net/, Authority: . Exception Message: Tried to get token using Managed Service Identity. Access token could not be acquired. MSI ResponseCode: BadRequest, Response: 
    Parameters: Connection String: [No connection string specified], Resource: https://database.windows.net/, Authority: . Exception Message: Tried to get token using Visual Studio. Access token could not be acquired. Visual Studio Token provider file not found at "D:\local\LocalAppData\.IdentityService\AzureServiceAuth\tokenprovider.json"
    Parameters: Connection String: [No connection string specified], Resource: https://database.windows.net/, Authority: . Exception Message: Tried to get token using Azure CLI. Access token could not be acquired. 'az' is not recognized as an internal or external command,
    operable program or batch file.
    
    
    at Microsoft.Azure.Services.AppAuthentication.AzureServiceTokenProvider.GetAuthResultAsyncImpl(String authority, String resource, String scope)
    at Microsoft.Azure.Services.AppAuthentication.AzureServiceTokenProvider.GetAuthenticationResultAsync(String resource, String tenantId)
    at Microsoft.Azure.Services.AppAuthentication.AzureServiceTokenProvider.GetAccessTokenAsync(String resource, String tenantId)
    --- End of inner exception stack trace ---
    

    如果我们使用系统分配标识并将SQL AD管理员配置为所说的标识,那么它可以正常工作。

    有什么想法吗?

    提前谢谢

    1 回复  |  直到 6 年前
        1
  •  1
  •   David Browne - Microsoft    6 年前

    看起来,azureServiceTokenProvider不支持用户分配的托管标识,至少在此时不支持。azureServiceTokenProvider是本地HTTP端点上的一个包装器,为应用程序提供令牌。

    我正在研究这个问题,似乎您必须向端点提供用户分配的托管标识的clientID,才能获取令牌。AzureServiceTokenProvider没有办法做到这一点(至少我能理解)。

    用户分配的托管标识添加了 倍数 用户为应用程序分配的托管标识。因此,获取令牌的API需要指定所需的MSI、系统分配的MSI或 什么之中的一个 用户分配了MSI。HTTP端点执行此操作的方式是,除非指定clientID,否则它使用系统分配的msi。

    在任何情况下,您都可以直接点击令牌端点,并提供用户分配的MSI的clientID,如下所示:

    public async Task<String> GetToken(string resource, string clientId = null)
    {
        var endpoint = System.Environment.GetEnvironmentVariable("MSI_ENDPOINT", EnvironmentVariableTarget.Process);
        var secret = System.Environment.GetEnvironmentVariable("MSI_SECRET", EnvironmentVariableTarget.Process);
    
        if (string.IsNullOrEmpty(endpoint))
        {
            throw new InvalidOperationException("MSI_ENDPOINT environment variable not set");
        }
        if (string.IsNullOrEmpty(secret))
        {
            throw new InvalidOperationException("MSI_SECRET environment variable not set");
        }
    
        Uri uri;
        if (clientId == null)
        {
            uri = new Uri($"{endpoint}?resource={resource}&api-version=2017-09-01");
        }
        else
        {
            uri = new Uri($"{endpoint}?resource={resource}&api-version=2017-09-01&clientid={clientId}");
        }
    
        // get token from MSI
        var tokenRequest = new HttpRequestMessage()
        {
            RequestUri = uri,
            Method = HttpMethod.Get
        };
        tokenRequest.Headers.Add("secret", secret);
        var httpClient = new HttpClient();
    
        var response = await httpClient.SendAsync(tokenRequest);
    
        var body = await response.Content.ReadAsStringAsync();
        var result = JObject.Parse(body);
    
        string token = result["access_token"].ToString();
        return token;
    
    }