目录
需求目标原理与思路实现引入Identity组件添加认证服务使用JWT认证和定义授权方式引入认证授权中间件添加JWT配置增加认证用户Model实现认证服务CreateToken方法添加认证接口保护API资源验证验证1: 验证直接访问创建TodoList接口验证2: 获取Token验证3: 携带Token访问创建TodoList接口验证4: 更换Policy一点扩展总结参考资料需求
在.NET Web API开发中还有一个很重要的需求是关于身份认证和授权的,这个主题非常大,所以本文不打算面面俱到地介绍整个主题,而仅使用.NET框架自带的认证和授权中间件去实现基于JWT的身份认证和授权功能。一些关于这个主题的基本概念也不会花很多的篇幅去讲解,我们还是专注在实现上。
目标
为TodoList项目增加身份认证和授权功能。
原理与思路
为了实现身份认证和授权功能,我们需要使用.NET自带的Authentication和Authorization组件。在本文中我们不会涉及Identity Server的相关内容,这是另一个比较大的主题,因为许可证的变更,Identity Server 4将不再能够免费应用于盈利超过一定限额的商业应用中,详情见官网IdentityServer。微软同时也在将广泛使用的IdentityServer的相关功能逐步集成到框架中:ASP.NET Core 6 and Authentication Servers,在本文中同样暂不会涉及。
实现
引入Identity组件
我们在Infrastructure项目中添加以下Nuget包:
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="6.0.1" /><PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="6.0.1" />
并新建Identity目录用于存放有关认证和授权的具体功能,首先添加用户类ApplicationUser:
在 在 因为本篇文章我们没有使用集成的 在 我们准备使用创建 启动 得到了 请求获取Token的接口: 可以看到我们已经拿到了JWT Token,把这个Token放到JWT解析一下可以看到: 主要在 选择 修改 并修改创建 还是使用 得到了 告诉我们需要一个具有 那么到此为止,我们已经实现了基于.NET自带的Identity框架,发放Token,完成认证和授权的功能。 关于在.NET Web API项目中进行认证和授权的主题非常庞大,首先是认证的方式可以有很多种,除了我们在本文中演示的基于JWT Token的认证方式以外,还有OpenId认证,基于Azuwww.easck.comre Active Directory的认证,基于OAuth协议的认证等等;其次是关于授权的方式也有很多种,可以是基于角色的授权,可以是基于Claims的授权,可以是基于Policy的授权,也可以自定义更多的授权方式。然后是具体的授权服务器的实现,有基于 由于 在本文中,我们实现了基于JWT Token的认证和授权。下一篇文章我们来看看为什么需要以及如何实现Refresh Token机制。 IdentityServer ASP.NET Core 6 and Authentication Servers 以上就是.NET 6实现基于JWT的Identity功能方法详解的详细内容,更多关于.NET 6基于JWT的Identity功能的资料请关注我们其它相关文章!Applicatio引入认证授权中间件
Api项目的Program中,MapControllers上面引入:Program.cs// 省略其他...app.UseAuthentication();app.UseAuthorization();
添加JWT配置
appsettings.Development.json"JwtSettings": { "validIssuer": "TodoListApi", "validAudience": "http://localhost:5050", "expires": 5}增加认证用户Model
Application/Common/Models中添加用于用户认证的类型:UserForAuthentication.csusing System.ComponentModel.DataAnnotations;namespace TodoList.Application.Common.Models;public record UserForAuthentication{ [Required(ErrorMessage = "username is required")] public string? UserName { get; set; } [Required(ErrorMessage = "password is required")] public string? Password { get; set; }}实现认证服务CreateToken方法
IdentityServer组件,而是应用程序自己去发放Token,那就需要我们去实现CreateTokenAsync方法:IdentityService.cs// 省略其他...public async Task<string> CreateTokenAsync(){ var signingCredentials = GetSigningCredentials(); var claims = await GetClaims(); var tokenOptions = GenerateTokenOptions(signingCredentials, claims); return new JwtSecurityTokenHandler().WriteToken(tokenOptions);}private SigningCredentials GetSigningCredentials(){ // 出于演示的目的,我将SECRET值在这里fallback成了硬编码的字符串,实际环境中,最好是需要从环境变量中进行获取,而不应该写在代码中 var key = Encoding.UTF8.GetBytes(Environment.GetEnvironmentVariable("SECRET") ?? "TodoListApiSecretKey"); var secret = new SymmetricSecurityKey(key); return new SigningCredentials(secret, SecurityAlgorithms.HmacSha256);}private async Task<List<Claim>> GetClaims(){ // 演示了返回用户名和Role两类Claims var claims = new List<Claim> { new(ClaimTypes.Name, User!.UserName) }; var roles = await _userManager.GetRolesAsync(User); claims.AddRange(roles.Select(role => new Claim(ClaimTypes.Role, role))); return claims;}private JwtSecurityToken GenerateTokenOptions(SigningCredentials signingCredentials, List<Claim> claims){ // 配置JWT选项 var jwtSettings = _configuration.GetSection("JwtSettings"); var tokenOptions = new JwtSecurityToken ( jwtSettings["validIssuer"], jwtSettings["validAudience"], claims, expires: DateTime.Now.AddMinutes(Convert.ToDouble(jwtSettings["expires"])), signingCredentials: signingCredentials ); return tokenOptions;}添加认证接口
Api项目中新建一个Controller用于实现获取Token的接口:AuthenticationController.csusing Microsoft.AspNetCore.Mvc;using TodoList.Application.Common.Interfaces;using TodoList.Application.Common.Models;namespace TodoList.Api.Controllers;[ApiController]public class AuthenticationController : ControllerBase{ private readonly IIdentityService _identityService; private readonly ILogger<AuthenticationController> _logger; public AuthenticationController(IIdentityService identityService, ILogger<AuthenticationController> logger) { _identityService = identityService; _logger = logger; } [HttpPost("login")] public async Task<IActionResult> Authenticate([FromBody] UserForAuthentication userForAuthentication) { if (!await _identityService.ValidateUserAsync(userForAuthentication)) { return Unauthorized(); } return Ok(new { Token = await _identityService.CreateTokenAsync() }); }}保护API资源
TodoList接口来演示认证和授权功能,所以添加属性如下:// 省略其他...[HttpPost]// 演示使用Policy的授权[Authorize(Policy = "OnlyAdmin")][ServiceFilter(typeof(LogFilterAttribute))]public async Task<ApiResponse<Domain.Entities.TodoList>> Create([FromBody] CreateTodoListCommand command){ return ApiResponse<Domain.Entities.TodoList>.Success(await _mediator.Send(command));}验证
验证1: 验证直接访问创建TodoList接口
Api项目,直接执行创建TodoList的请求:
401 Unauthorized结果。验证2: 获取Token


payload中可以看到两个Claims和其他配置的信息。验证3: 携带Token访问创建TodoList接口
Bearer Token验证方式并填入获取到的Token,再次请求创建TodoList:
验证4: 更换Policy
Infrastructure/DependencyInjection.cs// 省略其他...// 添加授权Policy是基于角色的,策略名称为OnlyAdmin,策略要求具有Administrator角色services.AddAuthorization(options =>{ options.AddPolicy("OnlyAdmin", policy => policy.RequireRole("Administrator")); options.AddPolicy("OnlySuper", policy => policy.RequireRole("SuperAdmin"));});TodoList接口的授权Policy:// 省略其他...[Authorize(Policy = "OnlySuper")]
admin@locahost用户的用户名和密码获取最新的Token后,携带Token请求创建新的TodoList:
403 Forbidden返回,并且从日志中我们可以看到:
SuperAdmin角色的用户的合法Token才会被授权。一点扩展
Identity Server 4的实现,当然在其更改过协议后,我们可以转而使用.NET中移植进来的IdentityServer组件实现,配置的方式也有很多。IdentityServer涉及的知识点过于庞杂,所以本文并没有试图全部讲到,考虑后面单独出一个系列来讲关于IdentityServer在.NET 6 Web API开发中的应用。总结
参考资料








