您现在的位置是:网站首页> 编程资料编程资料

.NET 6实现基于JWT的Identity功能方法详解_实用技巧_

2023-05-24 239人已围观

简介 .NET 6实现基于JWT的Identity功能方法详解_实用技巧_

需求

在.NET Web API开发中还有一个很重要的需求是关于身份认证和授权的,这个主题非常大,所以本文不打算面面俱到地介绍整个主题,而仅使用.NET框架自带的认证和授权中间件去实现基于JWT的身份认证和授权功能。一些关于这个主题的基本概念也不会花很多的篇幅去讲解,我们还是专注在实现上。

目标

TodoList项目增加身份认证和授权功能。

原理与思路

为了实现身份认证和授权功能,我们需要使用.NET自带的AuthenticationAuthorization组件。在本文中我们不会涉及Identity Server的相关内容,这是另一个比较大的主题,因为许可证的变更,Identity Server 4将不再能够免费应用于盈利超过一定限额的商业应用中,详情见官网IdentityServer。微软同时也在将广泛使用的IdentityServer的相关功能逐步集成到框架中:ASP.NET Core 6 and Authentication Servers,在本文中同样暂不会涉及。

实现

引入Identity组件

我们在Infrastructure项目中添加以下Nuget包:

并新建Identity目录用于存放有关认证和授权的具体功能,首先添加用户类ApplicationUser

ApplicationUser.cs

using Microsoft.AspNetCore.Identity; namespace TodoList.Infrastructure.Identity; public class ApplicationUser : IdentityUser { // 不做定制化实现,仅使用原生功能 } 

由于我们希望使用现有的SQL Server数据库来存储认证相关的信息,所以还需要修改DbContext:

TodoListDbContext.cs

public class TodoListDbContext : IdentityDbContext { private readonly IDomainEventService _domainEventService; public TodoListDbContext( DbContextOptions options, IDomainEventService domainEventService) : base(options) { _domainEventService = domainEventService; } // 省略其他... } 

为了后面演示的方便,我们还可以在添加种子数据的逻辑里增加内置用户数据:

TodoListDbContextSeed.cs

// 省略其他... public static async Task SeedDefaultUserAsync(UserManager userManager, RoleManager roleManager) { var administratorRole = new IdentityRole("Administrator"); if (roleManager.Roles.All(r => r.Name != administratorRole.Name)) { await roleManager.CreateAsync(administratorRole); } var administrator = new ApplicationUser { UserName = "admin@localhost", Email = "admin@localhost" }; if (userManager.Users.All(u => u.UserName != administrator.UserName)) { // 创建的用户名为admin@localhost,密码是admin123,角色是Administrator await userManager.CreateAsync(administrator, "admin123"); await userManager.AddToRolesAsync(administrator, new[] { administratorRole.Name }); } } 

并在ApplicationStartupExtensions中修改:

ApplicationStartupExtensions.cs

public static class ApplicationStartupExtensions { public static async Task MigrateDatabase(this WebApplication app) { using var scope = app.Services.CreateScope(); var services = scope.ServiceProvider; try { var context = services.GetRequiredService(); context.Database.Migrate(); var userManager = services.GetRequiredService>(); var roleManager = services.GetRequiredService>(); // 生成内置用户 await TodoListDbContextSeed.SeedDefaultUserAsync(userManager, roleManager); // 省略其他... } catch (Exception ex) { throw new Exception($"An error occurred migrating the DB: {ex.Message}"); } } } 

最后我们需要来修改DependencyInjection部分,以引入身份认证和授权服务:

DependencyInjection.cs

// 省略其他.... // 配置认证服务 // 配置认证服务 services .AddDefaultIdentity(o => { o.Password.RequireDigit = true; o.Password.RequiredLength = 6; o.Password.RequireLowercase = true; o.Password.RequireUppercase = false; o.Password.RequireNonAlphanumeric = false; o.User.RequireUniqueEmail = true; }) .AddRoles() .AddEntityFrameworkStores() .AddDefaultTokenProviders(); 

添加认证服务

Applicaiton/Common/Interfaces中添加认证服务接口IIdentityService

IIdentityService.cs

namespace TodoList.Application.Common.Interfaces; public interface IIdentityService { // 出于演示的目的,只定义以下方法,实际使用的认证服务会提供更多的方法 Task CreateUserAsync(string userName, string password); Task ValidateUserAsync(UserForAuthentication userForAuthentication); Task CreateTokenAsync(); } 

然后在Infrastructure/Identity中实现IIdentityService接口:

IdentityService.cs

namespace TodoList.Infrastructure.Identity; public class IdentityService : IIdentityService { private readonly ILogger _logger; private readonly IConfiguration _configuration; private readonly UserManager _userManager; private ApplicationUser? User; public IdentityService( ILogger logger, IConfiguration configuration, UserManager userManager) { _logger = logger; _configuration = configuration; _userManager = userManager; } public async Task CreateUserAsync(string userName, string password) { var user = new ApplicationUser { UserName = userName, Email = userName }; await _userManager.CreateAsync(user, password); return user.Id; } public async Task ValidateUserAsync(UserForAuthentication userForAuthentication) { User = await _userManager.FindByNameAsync(userForAuthentication.UserName); var result = User != null && await _userManager.CheckPasswordAsync(User, userForAuthentication.Password); if (!result) { _logger.LogWarning($"{nameof(ValidateUserAsync)}: Authentication failed. Wrong username or password."); } return result; } public async Task CreateTokenAsync() { // 暂时还不来实现这个方法 throw new NotImplementedException(); } } 

并在DependencyInjection中进行依赖注入:

DependencyInjection.cs

// 省略其他... // 注入认证服务 services.AddTransient(); 

现在我们来回顾一下已经完成的部分:我们配置了应用程序使用内建的Identity服务并使其使用已有的数据库存储;我们生成了种子用户数据;还实现了认证服务的功能。

在继续下一步之前,我们需要对数据库做一次Migration,使认证鉴权相关的数据表生效:

$ dotnet ef database update -p src/TodoList.Infrastructure/TodoList.Infrastructure.csproj -s src/TodoList.Api/TodoList.Api.csproj Build started... Build succeeded. [14:04:02 INF] Entity Framework Core 6.0.1 initialized 'TodoListDbContext' using provider 'Microsoft.EntityFrameworkCore.SqlServer:6.0.1' with options: MigrationsAssembly=TodoList.Infrastructure, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null # 创建相关数据表... [14:04:03 INF] Executed DbCommand (43ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] CREATE TABLE [AspNetRoles] ( [Id] nvarchar(450) NOT NULL, [Name] nvarchar(256) NULL, [NormalizedName] nvarchar(256) NULL, [ConcurrencyStamp] nvarchar(max) NULL, CONSTRAINT [PK_AspNetRoles] PRIMARY KEY ([Id]) ); # 省略中间的部分.. [14:04:03 INF] Executed DbCommand (18ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion]) VALUES (N'20220108060343_AddIdentities', N'6.0.1'); Done. 

运行Api程序,然后去数据库确认一下生成的数据表:

种子用户:

以及角色:

到目前为止,我已经集成了Identity框架,接下来我们开始实现基于JWT的认证和API的授权功能:

使用JWT认证和定义授权方式

Infrastructure项目的DependencyInjection中添加JWT认证配置:

DependencyInjection.cs

// 省略其他... // 添加认证方法为JWT Token认证 services .AddAuthentication(opt => { opt.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; opt.Default
                
                

-六神源码网