【问题标题】:Aspnet core web api protected with Azure受 Azure 保护的 Aspnet 核心 Web api
【发布时间】:2017-02-10 18:51:11
【问题描述】:

我的组织中有一个使用 aspnet 核心构建的 web api。我们希望发布该 api 以供 android 应用程序、mvc5 应用程序和 aspnet core mvc6 应用程序使用。如何在 azure 中配置 web api,以便使用它的应用程序不要求登录。 Web 应用程序已经受到 azure 的保护,但是当我使用 azure 保护 Web api 时,当我向它发出请求时会收到 401。我不知道如何在 azure 中配置应用程序或我必须在 api 中配置的代码。我已经阅读了很多,但我没有找到实现这一点的方法。我想要的只是登录我的网络应用程序,然后网络应用程序开始通过 ajax 向网络 API 询问数据。我应该在 ajax 请求中发送某种裸令牌,但我不知道我必须在 azure 和应用程序中做什么配置。我希望你能帮助我。

【问题讨论】:

标签: asp.net-web-api oauth-2.0 asp.net-core adal azure-active-directory


【解决方案1】:

在您使用 Azure AD 保护 Web API 后,我们需要发送访问令牌并请求 Web API 进行授权。当用户从 Web 应用程序调用 Web API 时,我们可以获取访问令牌。以下是在 Web 应用中获取令牌的代码供您参考:

public async Task<IActionResult> Index()
{
        AuthenticationResult result = null;
        List<TodoItem> itemList = new List<TodoItem>();

        try
        {
            string userObjectID = (User.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier"))?.Value;
            AuthenticationContext authContext = new AuthenticationContext(Startup.Authority, new NaiveSessionCache(userObjectID, HttpContext.Session));
            ClientCredential credential = new ClientCredential(Startup.ClientId, Startup.ClientSecret);
            result = await authContext.AcquireTokenSilentAsync(Startup.TodoListResourceId, credential, new UserIdentifier(userObjectID, UserIdentifierType.UniqueId));

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

            //
            // Return the To Do List in the view.
            //
            if (response.IsSuccessStatusCode)
            {
                List<Dictionary<String, String>> responseElements = new List<Dictionary<String, String>>();
                JsonSerializerSettings settings = new JsonSerializerSettings();
                String responseString = await response.Content.ReadAsStringAsync();
                responseElements = JsonConvert.DeserializeObject<List<Dictionary<String, String>>>(responseString, settings);
                foreach (Dictionary<String, String> responseElement in responseElements)
                {
                    TodoItem newItem = new TodoItem();
                    newItem.Title = responseElement["title"];
                    newItem.Owner = responseElement["owner"];
                    itemList.Add(newItem);
                }

                return View(itemList);
            }
            else
            {
                //
                // If the call failed with access denied, then drop the current access token from the cache, 
                //     and show the user an error indicating they might need to sign-in again.
                //
                if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
                {
                    var todoTokens = authContext.TokenCache.ReadItems().Where(a => a.Resource == Startup.TodoListResourceId);
                    foreach (TokenCacheItem tci in todoTokens)
                        authContext.TokenCache.DeleteItem(tci);

                    ViewBag.ErrorMessage = "UnexpectedError";
                    TodoItem newItem = new TodoItem();
                    newItem.Title = "(No items in list)";
                    itemList.Add(newItem);
                    return View(itemList);
                }
            }
        }
        catch (Exception ee)
        {
            if (HttpContext.Request.Query["reauth"] == "True")
            {
                //
                // Send an OpenID Connect sign-in request to get a new set of tokens.
                // If the user still has a valid session with Azure AD, they will not be prompted for their credentials.
                // The OpenID Connect middleware will return to this controller after the sign-in response has been handled.
                //
                return new ChallengeResult(OpenIdConnectDefaults.AuthenticationScheme);
            }

            //
            // The user needs to re-authorize.  Show them a message to that effect.
            //
            TodoItem newItem = new TodoItem();
            newItem.Title = "(Sign-in required to view to do list.)";
            itemList.Add(newItem);
            ViewBag.ErrorMessage = "AuthorizationRequired";
            return View(itemList);
        }


        //
        // If the call failed for any other reason, show the user an error.
        //
        return View("Error");
}

以下是使用 JwtBearerAppBuilderExtensions 将 OpenIdConnect Bearer 身份验证功能添加到 HTTP 应用程序管道以供 Web API 验证令牌的代码示例:

public class Startup
{
        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {
            // Add the console logger.
            loggerFactory.AddConsole(LogLevel.Debug);

            // Configure the app to use Jwt Bearer Authentication
            app.UseJwtBearerAuthentication(new JwtBearerOptions
            {
                AutomaticAuthenticate = true,
                AutomaticChallenge = true,
                Authority = String.Format(Configuration["AzureAd:AadInstance"], Configuration["AzureAD:Tenant"]),
                Audience = Configuration["AzureAd:Audience"],
            });
        }
}

您可以参考here的完整代码示例。

注意:要成功运行这个示例,我们需要将TitleOwner修改为小写titleowner 在 web 应用的 ToDoController 中:

 foreach (Dictionary<String, String> responseElement in responseElements)
 {
     TodoItem newItem = new TodoItem();
     newItem.Title = responseElement["title"];
     newItem.Owner = responseElement["owner"];
     itemList.Add(newItem);
 }

【讨论】:

  • 我已经看过这个样本。在这种情况下,当应用程序以编程方式调用 api 时,用户不是第一次要求他的凭据吗?这就是我想要避免的。
  • 在这种情况下,用户只需输入他们的凭据即可在应用程序中登录。用户登录后,无需用户再次输入凭证即可获取web api的token。这符合您的情况吗?
  • @snekkke 我不明白:“用户不是要求提供他的凭据吗......”你想支持什么场景?
  • 我想支持客户端凭据授予流程。我缺少的部分是在 azure 中创建一个客户端应用程序并为该应用程序生成一个秘密。因此,使用客户端应用程序的客户端 ID 和客户端密码,我可以在不要求用户输入凭据(用户和密码)的情况下请求令牌。
【解决方案2】:

您可以使用 Azure OpenIdConnect 进行联合身份验证。下面是一篇来自微软的好文章 -

Calling a web API in a web app using Azure AD and OpenID Connect

【讨论】:

    猜你喜欢
    • 2019-05-03
    • 2020-02-19
    • 2017-05-23
    • 2022-08-22
    • 2021-07-22
    • 2017-11-02
    • 2017-12-19
    • 1970-01-01
    • 2014-10-29
    相关资源
    最近更新 更多