.Net 控制台应用程序调用 Web API,调用另一个 Web API

Posted

技术标签:

【中文标题】.Net 控制台应用程序调用 Web API,调用另一个 Web API【英文标题】:.Net Console app calling a Web API, calling another Web API 【发布时间】:2019-10-16 06:04:51 【问题描述】:

我很难理解如何重用用户的安全令牌,以便在他们的数据请求流经多个 Web API 时对其进行身份验证。

Console App - C# / Net Framework 4.7.x 控制台应用程序。 WebAPI 1 - C# / .Net Core 2.2 MVC WebAPI 应用程序。 WebAPI 2 - C# / .Net Core 2.2 MVC WebAPI 应用程序。

目前,这些都在我的开发盒上运行的自己的 Visual Studio 2019 解决方案中配置为独立应用程序,但是(一旦工作!!)每个都将作为单独的实体托管在 Azure 中。

基本上,用户在控制台应用程序中进行身份验证,从 Azure Active Directory 验证他们的凭据。在 GitHub 上关注this sample,我的控制台应用程序可以成功调用WebAPI 1,并返回数据。

但是,我希望 WebAPI 1 在通话期间调用 WebAPI 2 并检索其他数据作为控制台应用程序数据集的一部分,这是我坚持的部分。

WebAPI 2在Azure Portal中的配置与WebAPI 1完全一样,只是Application Client Id不同等等。

作为示例的一部分(如上所述),我可以让WebAPI 1 调用 Microsoft 的 Graph API,然后将数据返回给调用 Console App,所以我不认为我是一个方法离开这个。下面是调用 Graph API 的代码:

    public async Task<string> CallGraphApiOnBehalfOfUser()
    
        string[] scopes =  "user.read" ;

        // we use MSAL.NET to get a token to call the API On Behalf Of the current user
        try
        
            string accessToken = await _tokenAcquisition.GetAccessTokenOnBehalfOfUser(HttpContext, scopes);
            dynamic me = await CallGraphApiOnBehalfOfUser(accessToken);
            return me.userPrincipalName;
        
        catch (MsalUiRequiredException ex)
        
            _tokenAcquisition.ReplyForbiddenWithWwwAuthenticateHeader(HttpContext, scopes, ex);
            return string.Empty;
        
     

    private static async Task<dynamic> CallGraphApiOnBehalfOfUserOriginal(string accessToken)
    
        //
        // Call the Graph API and retrieve the user's profile.
        //
        HttpClient client = new HttpClient();
        client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
        HttpResponseMessage response = await client.GetAsync("https://graph.microsoft.com/v1.0/me");
        string content = await response.Content.ReadAsStringAsync();
        if (response.StatusCode == HttpStatusCode.OK)
        
            dynamic me = JsonConvert.DeserializeObject(content);
            return me;
        

        throw new Exception(content);
    

我的计划是把上面代码中的URL改成指向WebAPI 2的地址,但是在认证过程中失败了。 如果我在WebAPI 2 中删除我的控制器上的 [Authorize] 类属性,它确实成功建立连接并返回预期的数据,但使用该属性,它甚至不会遇到断点在控制器上,向我建议问题出在我尝试使用的不记名令牌 WebAPI 2 配置不正确。

获取安全令牌的副本并尝试重新使用此飞行中途也不起作用,因为我假设该令牌用于WebAPI 1,因此不适用于WebAPI 2

我应该像这样进行传递身份验证吗? (将用户凭据硬编码到能够访问WebAPI 2WebAPI 1 感觉很脏,所以我不想这样做。另外,如果用户凭据需要更改,我有一个重新部署只是为了那个。)

有没有更好的方法来做我想做的事情?

如果您需要我提供更多信息来解释我所做的任何事情,我当然可以做到。

更新 1:这是 Startup.cs:

    public void ConfigureServices(IServiceCollection services)
    
        services.AddProtectWebApiWithMicrosoftIdentityPlatformV2(Configuration)
                .AddProtectedApiCallsWebApis(Configuration, new string[]  "user.read", "offline_access" )
                .AddInMemoryTokenCaches();

        services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
    

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    
        ...

        app.UseHttpsRedirection();
        app.UseAuthentication();
        app.UseMvc();
    

更新 2: 类似的 Stack Overflow 帖子 从那以后我找到了this SO post,@philippe-signoret 在他的回答中描述了它,这正是我所追求的。

更新 3: 调用 WebAPI 2 时未经授权的响应

这是我从通话中收到的错误消息:

StatusCode: 401, ReasonPhrase: 'Unauthorized', Version: 1.1, Content: System.Net.Http.HttpConnection+HttpConnectionResponseContent, Headers:

   Server: Kestrel
   WWW-Authenticate: Bearer error="invalid_token", error_description="The signature is invalid"
   X-SourceFiles: =?UTF-8?B?*<random-looking-code>*
   X-Powered-By: ASP.NET
   Date: Fri, 31 May 2019 09:48:31 GMT
   Content-Length: 0

如前所述,如果我从 Controller 的类中删除 [Authorize] 属性,调用将按预期进行。

【问题讨论】:

ConfigureService 的实现是什么?特别是 AddAuthorization 涉及此问题细节的最新示例:azure.microsoft.com/en-us/resources/samples/… @ranton187 - 在问题中添加了一些额外的代码来显示这一点。谢谢。 您有 2 种可能的情况 1) 您想在登录控制台应用程序并调用 WebAPI1 的当前用户的上下文中调用 WebAPI2.. 在这种情况下,您可以使用 On代表流程 2) WebAPI2 不关心用户并隐式信任 WebAPI1.. 在这种情况下,您可以使用 Client Credentials Grant 流程.. @RohitSaigal 谢谢,但这是我正在尝试代表用户的调用,并且包含在上述问题的代码中,但我坚持让它工作,尽管它调用第二个 WebAPI,它似乎不正确。或者我的配置不正确,这种可能性更大。 【参考方案1】:

您应该参考的示例是https://github.com/Azure-Samples/active-directory-dotnet-webapp-webapi-openidconnect-aspnetcore。此示例包含在受 Azure AD 保护的 ASP.NET Core 2.0 上运行的 Web API。 Web API 由 ASP.NET Core 2.0 Web 应用程序代表已登录用户访问。

示例中的中间应用是web应用,不是web api,但是底层原理是一样的。

我建议您首先严格遵循此示例,以便您了解如何代表用户从 web api 1 调用 web api 2。注意“Register the TodoListWebApp web application”部分下的第6、7步:

6, From the Settings blade, select Required permissions. Select + Add, and then select Select an API. 
Type TodoListService in the textbox and press Enter. Select the web API from the list and then select the Select button. Select Select Permissions. 
Tick the checkbox next to Access TodoListService and then select the Select button. Select the Done button.

7, In the Settings blade, under API Access, select Required permissions. 
Click on the Grant Permissions and when prompted press Yes. 
Once the web app is granted access to the webapi you should see the following message: Successfully granted permissions to the application for your account. 
To grant permissions for all users, please have an admin consent to the application.

在web应用中调用web api的核心代码sn-p如下:

            // Because we signed-in already in the WebApp, the userObjectId is know
            string userObjectID = (User.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier"))?.Value;

            // Using ADAL.Net, get a bearer token to access the TodoListService
            AuthenticationContext authContext = new AuthenticationContext(AzureAdOptions.Settings.Authority, new NaiveSessionCache(userObjectID, HttpContext.Session));
            ClientCredential credential = new ClientCredential(AzureAdOptions.Settings.ClientId, AzureAdOptions.Settings.ClientSecret);
            result = await authContext.AcquireTokenSilentAsync(AzureAdOptions.Settings.TodoListResourceId, credential, new UserIdentifier(userObjectID, UserIdentifierType.UniqueId));

            // Retrieve the user's To Do List.
            HttpClient client = new HttpClient();
            HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, AzureAdOptions.Settings.TodoListBaseAddress + "/api/todolist");
            request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken);
            HttpResponseMessage response = await client.SendAsync(request);

【讨论】:

以上是关于.Net 控制台应用程序调用 Web API,调用另一个 Web API的主要内容,如果未能解决你的问题,请参考以下文章

如何正确执行 RPC 样式的 Asp.Net Web API 调用?

Azure Active Directory:web api调用另一个web api

如何在asp.net核心中从客户端调用web api方法?

来自 ASP NET Web API 的 CORS 阻止 Ajax POST 调用 - 对预检请求的响应未通过访问控制检查

无法在 ASP.Net Core Web api 中启用 CORS

ASP.NET Web API 从 MVC 4 控制器失败