简体   繁体   English

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

[英].Net Console app calling a Web API, calling another Web API

I am having difficulty in understanding how to reuse a user's security token, to authenticate them as their data request flows through multiple Web APIs. 我很难理解如何重用用户的安全令牌,以便在数据请求流经多个Web API时对其进行身份验证。

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

Currently, these are all configured as standalone applications in their own Visual Studio 2019 Solutions running on my dev box, but (once working!!) will each be hosted in Azure as separate entities in their own right. 目前,这些都是在我自己的开发盒上运行的Visual Studio 2019解决方案中配置为独立应用程序,但是(一旦工作!!)将分别作为独立的实体托管在Azure中。

Essentially, the user authenticates within the Console App, validating their credentials from Azure Active Directory. 实质上,用户在控制台应用程序中进行身份验证,从Azure Active Directory验证其凭据。 Following this sample on GitHub, I've got my Console App to call off to WebAPI 1 successfully, and returns data. 在GitHub上的这个示例之后,我已经使用我的控制台应用程序成功调用WebAPI 1并返回数据。

However, I want WebAPI 1 to call WebAPI 2 during the call and to retrieve other data as part of the dataset for the Console App, and this is the part I'm stuck with. 但是,我希望WebAPI 1在调用期间调用WebAPI 2并检索其他数据作为Console App数据集的一部分,这是我坚持的部分。

WebAPI 2 is configured in the Azure Portal exactly the same as WebAPI 1 , with the exception of different Application Client Ids, etc. WebAPI 2在Azure门户中配置与WebAPI 1完全相同,但不同的应用程序客户端ID等除外。

As part of the sample (mentioned above), I am able to have WebAPI 1 call off to Microsoft's Graph API, before returning data back to the calling Console App , so I don't think I'm a way off of this. 作为示例的一部分(如上所述),我可以在将数据返回到调用Console App之前将WebAPI 1调用到Microsoft的Graph API,因此我认为我没有办法解决这个问题。 Here's the code to call the Graph API: 以下是调用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);
    }

My plan was to change the URL in the above code to point to the address of WebAPI 2 , but it fails during authentication. 我的计划是将上面代码中的URL更改为指向WebAPI 2的地址,但在身份验证期间失败。 IF I remove the [Authorize] class attribute on my Controller within WebAPI 2 , it does successfully make a connection and return the expected data, but with the attribute on, it doesn't even hit a breakpoint on the Controller, suggesting to me that the problem is with the bearer token that I'm trying to use OR that WebAPI 2 is not configured properly. 如果我WebAPI 2 删除了我的控制器上的[Authorize]类属性,它确实成功建立连接并返回预期的数据,但是如果属性打开,它甚至没有在Controller上找到断点,向我建议问题在于我正在尝试使用的承载令牌, 或者没有正确配置WebAPI 2。

Getting a copy of the security token and trying to re-use this mid-flight also doesn't work, as I assume that the token is for the WebAPI 1 and is therefore invalid for use with WebAPI 2 . 获取安全令牌的副本并尝试重新使用此中途飞行也不起作用,因为我假设该令牌用于WebAPI 1 ,因此WebAPI 2WebAPI 2一起使用。

Should I be doing pass-along authentication like this? 我应该像这样做传递身份验证吗? (It feels dirty to hard-code user credentials into WebAPI 1 which are able to access WebAPI 2 , so I don't want to do that. Plus, if the user credentials need changing, I've got a redeployment just for that.) (将用户凭据硬编码到能够访问WebAPI 2 WebAPI 1感觉很脏,所以我不想这样做。另外,如果用户凭据需要更改,我就会进行重新部署。 )

Is there a better way to do what I'm trying to do? 有没有更好的方法来做我想做的事情?

If you need me to provide more information to explain anything I've done, I certainly can do. 如果你需要我提供更多信息来解释我所做的一切,我当然可以做到。

UPDATE 1: Here's the Startup.cs: 更新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();
    }

UPDATE 2: Similar Stack Overflow post I've since found this SO post , which @philippe-signoret describes in his answer and is exactly what I'm after. 更新2:类似Stack Overflow帖子我发现了这个SO帖子 ,@ philippe-signoret在他的回答中描述,正是我所追求的。

UPDATE 3: unauthorized response when calling WebAPI 2 更新3:调用WebAPI 2时未经授权的响应

Here's the error message i get back from the call: 这是我从通话中收到的错误消息:

{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
}}

As I mentioned earlier, if I remove the [Authorize] attribute from my Controller's class, the call goes through as expected. 正如我之前提到的,如果我从Controller的类中删除[Authorize]属性,则调用将按预期进行。

The sample you should refer to is https://github.com/Azure-Samples/active-directory-dotnet-webapp-webapi-openidconnect-aspnetcore . 您应该参考的示例是https://github.com/Azure-Samples/active-directory-dotnet-webapp-webapi-openidconnect-aspnetcore This sample contains a web API running on ASP.NET Core 2.0 protected by Azure AD. 此示例包含在Azure AD保护的ASP.NET Core 2.0上运行的Web API。 The web API is accessed by an ASP.NET Core 2.0 web application on behalf of the signed-in user. ASP.NET Core 2.0 Web应用程序代表登录用户访问Web API。

The middle application in the sample is web application, not web api, but the underlying principle is same. 示例中的中间应用程序是Web应用程序,而不是web api,但基本原理是相同的。

I would suggest you strictly follow this sample first so you understand how to call the web api 2 from web api 1 on behalf of the users. 我建议您先严格遵循此示例,以便了解如何代表用户从web api 1调用web api 2。 Pay attention to step 6, 7 under " Register the TodoListWebApp web application " section: 请注意“ 注册TodoListWebApp Web应用程序 ”部分下的步骤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.

The core code snippet to call web api in web application is as below: 在Web应用程序中调用web api的核心代码片段如下:

            // 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);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM