本文翻译自: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部分-更新Web App身份验证代码
- 第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 所需的验证传入访问令牌。