Web应用程序和API AzureAD身份验证流程ASP.NET Core

时间:2018-08-07 06:12:31

标签: c# authentication asp.net-core asp.net-core-mvc azure-active-directory

我目前对如何实现身份验证/授权流程感到困惑。

我正在开发两个应用程序,一个是前端/ Web应用程序,另一个是后端/ API,都使用ASP.NET Core。目标是使用AzureAD并使用域中的用户/组。我已经在两个应用程序上都实现了身份验证,并且能够根据登录状态登录和限制内容。

作为参考,我以Microsoft开发人员的this为例。我应该做的正是这件事。有一个WebApp和API。使用的身份验证流程是授权代码流程。首先,用户需要登录,然后,当需要从API请求一些数据时,将请求访问令牌。

问题1:这是正确的身份验证流程吗?对我来说,这似乎是双重身份验证,因为首先我在前端对自己进行身份验证,当Webapp需要一些数据时,我需要在后端再次进行身份验证。使用相同的Azure AD租户,那么您在这里怎么看?

接下来的一点看起来非常“丑陋”是获取一些数据的过程。在该示例中,当首先请求某些数据时,将请求令牌,然后请求数据。但是我认为有很多样板。对于所有待办事项中的一个请求,仅需以下示例代码。

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

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

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

        //
        // 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)
        {
            return ProcessUnauthorized(itemList, authContext);
        }
    }
    catch (Exception)
    {
        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");
}

问题2:如果Q1中的流程正确,是否存在“不太丑陋”的方法来访问数据?

1 个答案:

答案 0 :(得分:0)

我找到了解决此问题的合适方法。

我只是在multitenant-saas-guidance中使用了此示例中的方法,它的工作原理就像是魅力。