[译]使用Microsoft.Identity.Web和Azure AD 迁移ASP. NET中的旧身份验证代码

214 阅读4分钟

本文翻译自:dev.to/425show/mig… Mats kas

大约3年前,我为一个客户写了一些代码。这是一个样本解决方案。前端是一个直接的ASP. NET Core MVC应用程序。后端API是一个简单的。NET Core API.两者都使用开放ID连接(OIDC)和OAuth 2来验证用户和获取令牌,以便检索和更新待办事项列表(如此原始——我知道!)。然而,解决方案的“果汁”就在验证码里。因此,在这篇博客文章中,我们将看看如何取出“旧”身份验证代码,并用ASP. NET Core-Microsoft中最新和最棒的代码替换它。身份。Web.博客分为两部分:

  1. 第1部分-更新Web App身份验证代码
  2. 第2部分-更新Web API验证代码

第1部分将重点介绍如何升级前端ASP. NET MVC Web应用程序的身份验证:

更新Web App

多亏了微软的创新工作。身份。Web,迁移将允许我们删除大量现有代码。任何与OIDC有关的东西现在都可以走了。大部分工作将集中在将项目升级到。NET 5和拉正确的依赖关系。下图显示了大部分工作的重点:

一旦我们删除了所有标记的文件,我们就可以继续并开始迁移。打开*. cs proj文件并从

<Project Sdk="Microsoft.NET.Sdk.Web">
  <PropertyGroup>
    <TargetFramework>netcoreapp2.0</TargetFramework>
    <UserSecretsId>aspnet-WebApp_OpenIDConnect_DotNet-81EA87AD-E64D-4755-A1CC-5EA47F49B5D8</UserSecretsId>
    <WebProject_DirectoryAccessLevelKey>0</WebProject_DirectoryAccessLevelKey>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.5" />
  </ItemGroup>
  <ItemGroup>
    <DotNetCliToolReference Include="Microsoft.Extensions.SecretManager.Tools" Version="2.0.0" />
    <DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.0.0" />
  </ItemGroup>
</Project>

对此:

<Project Sdk="Microsoft.NET.Sdk.Web">
  <PropertyGroup>
    <TargetFramework>net5.0</TargetFramework>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="5.0.0" NoWarn="NU1605" />
    <PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="5.0.0" NoWarn="NU1605" />
    <PackageReference Include="Microsoft.Identity.Web" Version="1.3.0" />
    <PackageReference Include="Microsoft.Identity.Web.UI" Version="1.3.0" />
  </ItemGroup>
  <ItemGroup>
    <DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.0.4" />
  </ItemGroup>
</Project>

接下来,我们需要更新appsettings.json来简化一些事情。用以下内容替换旧的配置:

{
    "AzureAd": {
    "Instance": "https://login.microsoftonline.com/",
    "Domain": "https://<TenantName>.onmicrosoft.com",
    "TenantId": "<YourTenantID>",
    "ClientId": "<YourClientID>",
    "CallbackPath": "/signin-oidc",
    "ClientSecret": "<YourClientSecret",    
    },
    "Logging": {
      "IncludeScopes": false,
      "LogLevel": {
          "Default": "Warning"
    }
  }
}

ASP. NET 5中的Startup.cs类已经发生了很大的变化,所以我们需要更新很多东西,而不仅仅是与身份验证相关的代码。首先,我们需要按照以下方式更新 Config reServices()方法:

public void ConfigureServices(IServiceCollection services)
{
    services.AddHttpClient();
    services.Configure<MicrosoftIdentityOptions>(options => options.ResponseType = OpenIdConnectResponseType.Code);
    services.AddMicrosoftIdentityWebAppAuthentication(Configuration)
        .EnableTokenAcquisitionToCallDownstreamApi()
        .AddInMemoryTokenCaches();
    services.AddControllersWithViews()
            .AddSessionStateTempDataProvider()
            .AddMicrosoftIdentityUI();
    services.AddSession();
}

首先,我们需要配置 Microsoft。身份。Web使用Azure AD进行身份验证,并设置缓存以存储API调用的访问令牌。The the.Add Microsoft denty UI指示中间件使用嵌入在M. I. W dll中的auth视图。

接下来,我们需要按照下面的代码更新 Configure() 方法:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseExceptionHandler("/Home/Error");
            app.UseHsts();
        }
        app.UseHttpsRedirection();
        app.UseStaticFiles();
        app.UseRouting();
        app.UseSession();
        app.UseAuthentication();
        app.UseAuthorization();
        app.UseEndpoints(endpoints =>
        {
           endpoints.MapControllerRoute(
                name: "default",
                pattern: "{controller=Home}/{action=Index}/{id?}");
        });
    }
}

我们还应该更新Program.cs。将整个代码替换为:

using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
namespace WebApp_OpenIDConnect_DotNet
{
    public class Program
    {
        public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }
        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                });
    }
}

随着中间件的排序,我们现在可以转到网络应用中的控制器和视图。首先,我们必须更新视图->共享->LoginPartial.cshtml视图,以便我们的登录和注销页面可以按预期工作。新代码应该是这样的:

@using System.Security.Principal
@if (User.Identity.IsAuthenticated)
{
    <ul class="nav navbar-nav navbar-right">
        <li class="navbar-text">Hello @User.Identity.Name!</li>
        <li><a asp-area="MicrosoftIdentity" asp-controller="Account" asp-action="SignOut">Sign out</a></li>
    </ul>
}
else
{
    <ul class="nav navbar-nav navbar-right">
        <li><a asp-area="MicrosoftIdentity" asp-controller="Account" asp-action="Signin">Sign in</a></li>
    </ul>
}

最后,我们需要修复我们的To Do控制器代码,因为很多事情已经改变了。我们控制器的更新代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using System.Text.Json;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Identity.Web;
using TodoListWebApp.Models;
using System.Text;
// For more information on enabling MVC for empty projects, visit http://go.microsoft.com/fwlink/?LinkID=397860
namespace TodoListWebApp.Controllers
{
    [Authorize]
    public class TodoController : Controller
    {
        private static ITokenAcquisition tokenGetter { get; set; }
        private static HttpClient httpClient;
        public TodoController(ITokenAcquisition tokenAcquisition, IHttpClientFactory clientFactory)
        {
            httpClient = clientFactory.CreateClient();
            tokenGetter = tokenAcquisition;
        }
        public async Task<IActionResult> Index()
        {
            var token = await tokenGetter.GetAccessTokenForUserAsync(new []{
                "api://9a080a62-e244-4adc-a7d9-dc98835c8815/access_as_user"
            });
            httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
            var options = new JsonSerializerOptions
            {
                PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
                WriteIndented = true
            };
            var result = await httpClient.GetAsync("https://localhost:44351/todolist");
            var content = await result.Content.ReadAsStringAsync();
            var todoItems = JsonSerializer.Deserialize<List<TodoItem>>(content);
            return View(todoItems);
        }
        [HttpPost]
        public async Task<ActionResult> Index(string item)
        {
            if (ModelState.IsValid)
            {
                var token = await tokenGetter.GetAccessTokenForUserAsync(new []{
                "api://9a080a62-e244-4adc-a7d9-dc98835c8815/access_as_user"
                });
                httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
                var todoItem = new TodoItem { Title=item, Owner = User.GetObjectId()};
                var content = new StringContent(JsonSerializer.Serialize(todoItem), Encoding.UTF8, "application/json");
                content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
                var result = await httpClient.PostAsync("https://localhost:44351/todolist",content);
                if(result.IsSuccessStatusCode)
                {
                    return RedirectToAction("Index");
                }
                else
                {
                    return View("Error");
                }
            }
            return View("Error");
        }
    }
}

首先,我们引入一个构造函数,这样我们就可以注入IT oken Aquiposition和IH ttp Client Factory对象,以便在代码的后面使用。IT oken Acquiption类允许我们在一行代码中轻松地从AzureAD/B2C获取访问令牌,而IH ttp Client Factory类用于提供Http Client并根据推荐的做法管理其生命周期。

获取访问令牌后,在向API发出请求之前,我们将其作为授权标头添加到HttpClient。

代码的其余部分要么获取To Do项,要么将其发布到API。

源代码

有一个完整的工作解决方案,显示了代码如何在GitHub上端到端地工作。

总结

这是一个无可辩驳的事实:当涉及到验证用户和获取或管理令牌时,Microsoft.Identity.Web 让开发人员的生活变得更加轻松。这篇博客文章证明,使用最新的库,我们可以编写更少的代码来保护应用程序免受Azure AD或B2C的攻击。在第2部分中,我们将研究迁移ASP. NET Core API以同时使用Microsoft.Identity.Web 所需的验证传入访问令牌。