.NET 8: 身份验证及授权的新特性

翻译文章,原文链接点我

.NET 8的发布即将到来。在为开发人员带来的惊人功能中,它在身份验证和授权支持方面进行了一次小的革命:将ASP.NET Core Identity从面向页面的方法转变为面向API的方法。

让我们探讨一下发生了什么。

ASP.NET Core Identity和基于令牌的身份验证

ASP.NET Core开发人员可以使用内置的ASP.NET Core Identity框架来添加对本地身份验证和授权的支持。身份框架包括开发人员通常需要的一切,用于对本地用户存储进行用户身份验证和授权。默认情况下,在Windows上创建一个SQL Server数据库,在macOS上创建一个SQLite数据库,但您可以将其更改为您选择的数据库管理系统(DBMS)。

ASP.NET Core Identity框架专为服务器呈现的Web应用程序设计,例如ASP.NET Core MVC或Razor Pages应用程序,并在单页应用程序(SPA)中存在一些挑战,其中基于令牌的身份验证似乎更为合适。为解决这个问题,自.NET Core 3.1起,Microsoft提供了基于Angular和React的SPA的内置项目模板,并支持Identity Server。然而,.NET社区对这种方法并不完全满意。为了满足社区的需求,Microsoft在.NET 8中移除了对Identity Server的默认支持,并重新设计了ASP.NET Core Identity的内部架构,使其更适用于SPA和本地应用程序。.NET 8引入了一组新的Identity API端点和对基于令牌的身份验证的支持。但让我们按顺序来看。

Bearer Token身份验证处理程序

这个新基础设施的第一个构建块是新的Bearer Token身份验证处理程序。该处理程序类似于ASP.NET Core Identity默认使用的经典Cookie身份验证处理程序。Cookie身份验证处理程序负责两件事:

  • 在用户验证后创建新的会话Cookie。
  • 根据接收到的传入HTTP请求中的有效会话Cookie构建一个ClaimsPrincipal用户对象。

同样,Bearer Token身份验证处理程序负责:

  • 在用户验证后创建新的令牌。
  • 根据接收到的传入HTTP请求中的有效令牌构建一个ClaimsPrincipal用户对象。

换句话说,Bearer Token处理程序模仿Cookie处理程序的行为,以基于令牌而不是Cookie来管理已验证的会话。

请注意,Bearer Token处理程序生成的令牌不是JWT格式的。而且,它们不遵循任何特定的标准。尽管文档提到了访问令牌刷新令牌,但它们并未实现OAuth 2.0授权框架

这个Bearer Token处理程序在实践中如何使用呢?

考虑以下代码:


// Program.cs

using Microsoft.AspNetCore.Authentication.BearerToken; //👈using System.Security.Claims;

var builder = WebApplication.CreateBuilder(args);

builder.Services
    .AddAuthentication()
    .AddBearerToken();  //👈
builder.Services.AddAuthorization();

var app = builder.Build();

app.MapGet("/login", (string username) =>
    {
        var claimsPrincipal = new ClaimsPrincipal(
          new ClaimsIdentity(
            new[] { new Claim(ClaimTypes.Name, username)},
            BearerTokenDefaults.AuthenticationScheme  //👈
          )
        );

        return Results.SignIn(claimsPrincipal);
    });

app.MapGet("/user", (ClaimsPrincipal user) =>
    {
        return Results.Ok($"Welcome {user.Identity.Name}!");
    })
    .RequireAuthorization();

app.Run();

该应用程序定义了两个API端点:

  • /login端点根据HTTP请求中发送的用户名创建一个ClaimsPrincipal用户对象,并返回签入的结果。
  • /user端点返回经过身份验证的用户的用户名。

请注意代码中的突出显示的行。您引用了新的Microsoft.AspNetCore.Authentication.BearerToken命名空间,这使您可以访问BearerTokenDefaults.AuthenticationScheme值。您还使用AddBearerToken()扩展方法配置了Bearer Token中间件。

现在,假设您使用curl调用登录端点,如下所示:

curl 'https://<YOUR_HOST>/login?username=joe'

你将会看到以下的结果:


{
  "token_type": "Bearer",
  "access_token": "CfDJ8Ha5YkqG...omitted content...",
  "expires_in": 3600,
  "refresh_token": "CfDJ8Ha5YkqG...omitted content..."
  }

这个JSON包含了一个访问令牌和一个刷新令牌,您可以使用它们来调用您应用程序提供的受保护的API。例如,您现在可以调用受保护的/user端点,如下所示:

curl -i https://<YOUR_HOST>/user \
-H 'Authorization: Bearer CfDJ8Ha5YkqG...omitted content...'

然后你会得到以下结果:

Welcome joe!

总的来说,Bearer Token身份验证处理程序使得建立基于令牌的身份验证变得非常容易。这是整个ASP.NET Core Identity过渡到基于令牌身份验证的基石。

身份验证API端点

在向ASP.NET Core Identity添加基于令牌的身份验证的第二步是引入身份验证API端点。基本上,这是ASP.NET Core Identity经典Web UI可以执行的操作的API版本。一旦启用了身份验证API端点,您将获得像/register/login/forgotPasswordconfirmEmail等端点。

身份验证API端点解决了两个最常抱怨的问题:

  • 您可以构建自己的用户身份验证和账户管理的UI,同时保持应用程序的整体UI风格。
  • 在身份验证后,您的应用程序可以获取访问令牌而不是Cookie,因此对于SPA和本地应用程序来说,这是一种更合适的方法。

请记住,身份验证API端点不实现OpenID Connect(OIDC)。它们只是通过自定义API公开了ASP.NET Core Identity UI提供的标准功能。它们用于第一方身份验证。

  • AddIdentityApiEndpoints() ,该代码使用Bearer Token处理程序配置基于令牌的身份验证,同时添加Cookie身份验证,并添加一组身份验证服务。
  • MapIdentityApi(),该代码实际上将身份验证端点添加到您的应用程序。

你的Program.cs 文件里的代码应该像如下所示:


// Program.cs

using System.Security.Claims;using Microsoft.AspNetCore.Identity;using Microsoft.AspNetCore.Identity.EntityFrameworkCore;using Microsoft.EntityFrameworkCore;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddAuthorization();

builder.Services.AddDbContext<ApplicationDbContext>(
    options => options.UseSqlite(builder.Configuration["ConnectionString"]));

builder.Services.AddIdentityApiEndpoints<IdentityUser>()
    .AddEntityFrameworkStores<ApplicationDbContext>();

var app = builder.Build();

app.MapIdentityApi<IdentityUser>();

app.MapGet("/user", (ClaimsPrincipal user) =>
    {
        return Results.Ok($"Welcome {user.Identity.Name}!");
    })
    .RequireAuthorization();

app.Run();

您的客户端将能够通过调用/register端点注册用户,通过/login端点对用户进行身份验证,等等。

尽管身份验证API端点似乎解决了关于经典ASP.NET Core Identity的两个主要问题,但在生产环境中使用它们仍存在一些关于安全性和可伸缩性方面的顾虑

Blazor UI 验证

除了对ASP.NET Core Identity的更改之外,.NET 8将身份验证框架集成到Blazor项目模板中。您可以通过运行以下命令创建一个具有身份验证支持的新.NET 8 Blazor应用程序:

dotnet new blazor -au Individual

您将获得一个Blazor应用程序,其中包含一切您需要在本地管理用户和进行身份验证的内容。您将获得一个SQLite数据库来存储用户帐户。如果您使用Visual Studio而不是.NET CLI对应用程序进行脚手架生成,您将获得一个SQL Server数据库。您还将获得所有允许用户注册、进行身份验证和管理其帐户的页面。

以下图片显示了Blazor应用程序的内置登录页面:

实现Blazor Identity UI的用户身份验证和管理页面不是像经典的ASP.NET Core Identity那样的Razor页面,而是Razor组件。模板为您提供了项目中Pages/Account文件夹中所有UI组件的源代码。这使您可以根据应用程序设计自定义它们。

自定义授权策略简化

除了与ASP.NET Core Identity相关的新功能之外,.NET 8还引入了对自定义授权策略定义的简化。以前,您必须编写大量代码来定义一个参数化的授权策略,以应用于API端点。

例如,假设您想要为一个仅授权18岁以上用户的端点定义一个策略,并且可以按以下方式使用:

[ApiController]
[Route("api/[controller]")]
public class DrinkController : Controller
{
    [MinimumAgeAuthorize(18)]

    [HttpGet("beer")]

    public string BeerDispenser(ClaimsPrincipal user) => $"Here is your beer, {user.Identity?.Name}!";
}

在之前的.NET版本中,为了实现自定义授权策略,您必须编写如下代码片段中概述的代码:


class MinimumAgeAuthorizeAttribute : AuthorizeAttribute
{
  ...
}

class MinimumAgePolicyProvider : IAuthorizationPolicyProvider
{
  ...
}

class MinimumAgeRequirement : IAuthorizationRequirement
{
  ...
}

class MinimumAgeAuthorizationHandler : AuthorizationHandler<MinimumAgeRequirement>
{
  ..
.}

您必须定义一个AuthorizeAttribute。然后,您需要实现一个AuthorizationPolicyProvider来处理您的自定义策略,以及一个AuthorizationRequirement来定义需求。最后,您必须实现AuthorizationHandler

面显示的代码片段概述了您需要实现的类。要查看实际实现的示例,请看这个代码样例

使用新的IAuthorizationRequirementData接口,您的实现代码变成如下:


class MinimumAgeAuthorizeAttribute : AuthorizeAttribute, IAuthorizationRequirement, IAuthorizationRequirementData{
  public MinimumAgeAuthorizeAttribute(int age) => Age =age;
  public int Age { get; }

  public IEnumerable<IAuthorizationRequirement> GetRequirements()
  {
    yield return this;
  }}

class MinimumAgeAuthorizationHandler : AuthorizationHandler<MinimumAgeAuthorizeAttribute>{
  protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, MinimumAgeAuthorizeAttribute requirement) 
  {
    ...
  }}

您只需实现两个类,而不是像前面的示例中那样的四个类。查看此处获取完整的代码。

总结

在本文中,您了解了.NET 8为支持身份验证和授权带来的新功能。其中大多数功能与扩展ASP.NET Core Identity框架以支持基于令牌的身份验证有关,为SPA和本地应用程序的身份验证铺平了道路。

作为一个副产品,您可以访问Bearer Token身份验证处理程序,它允许您发放访问令牌,以及身份验证API端点,它允许您在Web API中嵌入身份验证支持。

此外,Blazor项目模板现在通过Identity API端点支持身份验证,还提供了一套丰富的Razor组件,专为用户身份验证和管理而设计。

最后,新的IAuthorizationRequirementData接口使您能够以更少的代码创建自定义授权策略。

发表回复

您的电子邮箱地址不会被公开。