我们开发的API必须做鉴权和授权操作,否则,就成了裸跑了,那对于系统来说是很危险的。总体来说,一般的WebAPI和MinimalAPI鉴权的过程都是差不多的,但是也有微小的差异。今天我们就来详细说一下,内容很简单,大家有了基础可以随意去扩展。
1、第一个项目:PatrickLiu.Net6API.AuthenticationCenter,用于提供获取 Token 接口。
// 堆代码 duidaima.com using PatrickLiu.Net6API.Extensions; var builder = WebApplication.CreateBuilder(args); builder.Services.AddControllers(); builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); builder.Services.Configure<JWTTokenOption>(builder.Configuration.GetSection("JWTTokenOption")); builder.Services.AddTransient<IJWTService, JWTService>(); var app = builder.Build(); app.UseSwagger(); app.UseSwaggerUI(); app.MapControllers(); app.Run();(2)、AuthenticationController 源码如下:
using Microsoft.AspNetCore.Mvc; using Newtonsoft.Json; using PatrickLiu.Net6API.Extensions; namespace PatrickLiu.Net6API.AuthenticationCenter.Controllers { [ApiController] [Route("api/[controller]/[action]")] public class AuthenticationController : ControllerBase { private readonly IJWTService _jWTService; /// <summary> /// 实例化。 /// </summary> /// <param name="jWTService">注入服务。</param> public AuthenticationController(IJWTService jWTService) { _jWTService = jWTService; } /// <summary> /// 堆代码 duidaima.com /// 根据用户名和密码获取 Token。 /// </summary> /// <param name="userName">用户名。</param> /// <param name="password">密码。</param> /// <returns></returns> [HttpPost] public string Login(string userName, string password) { if (!string.IsNullOrEmpty(userName) && !string.IsNullOrEmpty(password)) { if (string.Compare(userName, "PatrickLiu", true) == 0 && string.Compare(password, "liulei123456", true) == 0) { string token = _jWTService.GetToken(userName, password); return JsonConvert.SerializeObject(new { Result = true, Token = token }); } } return string.Empty; } } }(3)、appsettings.json 配置文件源码如下,红色字体要注意:
{ "Logging": { "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Warning" } }, "AllowedHosts": "*", "JWTTokenOption": { "Audience": "PatrickLiu.com", "Issuer": "PatrickLiu.com", "SecurityKey": "12333456677655ffrrffff" } }
2、还有一个公共项目,用于存放公共类型。项目类型:PatrickLiu.Net6API.Extensions,项目类型:Net 6.0类库。
namespace PatrickLiu.Net6API.Extensions { /// <summary> /// 提供 Token 的服务。 /// </summary> public interface IJWTService { /// <summary> /// 获取 Token。 /// </summary> /// <param name="userName">用户名</param> /// <param name="password">密码</param> /// <returns></returns> string GetToken(string userName,string password); } }(2)、JWTService 源码如下:
using Microsoft.Extensions.Options; using Microsoft.IdentityModel.Tokens; using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using System.Security.Cryptography; using System.Text; namespace PatrickLiu.Net6API.Extensions { /// <summary> /// 提供 Token 服务的实现。 /// </summary> public sealed class JWTService : IJWTService { private readonly IOptionsMonitor<JWTTokenOption> _option; /// <summary> /// 实例化。 /// </summary> /// <param name="option">注入选项。</param> public JWTService(IOptionsMonitor<JWTTokenOption> option) { _option = option; } /// <summary> /// 获取 Token。 /// </summary> /// <param name="userName">用户名。</param> /// <param name="password">密码</param> /// <returns></returns> public string GetToken(string userName, string password) { #region 有效载荷 var claims = new[] { new Claim(ClaimTypes.Name, userName), new Claim("NickName",userName), new Claim(ClaimTypes.Role,"Administrator"), new Claim("Password",password), }; #endregion SymmetricSecurityKey key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_option.CurrentValue.SecurityKey!)); SigningCredentials signingCredentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); JwtSecurityToken token = new JwtSecurityToken( issuer: _option.CurrentValue.Issuer!, audience: _option.CurrentValue.Audience!, claims: claims, expires: DateTime.Now.AddMinutes(5), signingCredentials: signingCredentials ); string returnToken = new JwtSecurityTokenHandler().WriteToken(token); return returnToken; } } }(3)、JWTTokenOption 源码如下:
namespace PatrickLiu.Net6API.Extensions { /// <summary> /// 用于接受配置数据实体类型。 /// </summary> public sealed class JWTTokenOption { /// <summary> /// 获取或者设置接受者。 /// </summary> public string? Audience { get; set; } /// <summary> /// 获取或者设置加密 key。 /// </summary> public string? SecurityKey { get; set; } /// <summary> /// 获取或者设置发布者 /// </summary> public string? Issuer { get; set; } } }(4)、授权服务器运行起来如下:
Microsoft.AspNetCore.Authentication.JwtBearer Microsoft.IdentityModel.Tokens(3)、Program.cs源码如下,红色部分表示健全和授权的重要部分。
1 using Microsoft.AspNetCore.Authentication.JwtBearer; 2 using Microsoft.IdentityModel.Tokens; 3 using PatrickLiu.Net6API.Extensions; 4 using System.Text; 5 6 var builder = WebApplication.CreateBuilder(args); 7 8 builder.Services.AddControllers(); 9 builder.Services.AddEndpointsApiExplorer(); 10 builder.Services.AddSwaggerGen(); 11 12 #region 配置鉴权 13 14 //增加的鉴权逻辑,角色认证、策略认证都是支持的,和Net Core MVC 支持的一样。 15 JWTTokenOption tokenOption = new JWTTokenOption(); 16 builder.Configuration.Bind("JWTTokenOption", tokenOption); 17 //builder.Services.Configure<JWTTokenOption>(builder.Configuration.GetSection("JWTTokenOption")); 18 builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) 19 .AddJwtBearer(option => 20 { 21 option.TokenValidationParameters = new TokenValidationParameters() 22 { 23 ValidateIssuer = true,//是否验证 Issuer(发行商) 24 ValidateAudience = true,//是否验证 Audience(受众者) 25 ValidateLifetime = true,//是否验证失效时间 26 ValidateIssuerSigningKey = true,//是否验证 Issuer 的签名键 27 ValidAudience=tokenOption.Audience, 28 ValidIssuer=tokenOption.Issuer,// ValidAudience,ValidIssuer这两项的值要和验证中心的只保持一致。 29 IssuerSigningKey=new SymmetricSecurityKey(Encoding.UTF8.GetBytes(tokenOption.SecurityKey!)) 30 }; 31 }); 32 33 #endregion 34 35 var app = builder.Build(); 36 37 app.UseSwagger(); 38 app.UseSwaggerUI(); 39 40 app.UseAuthentication(); 41 app.UseAuthorization();42 43 app.MapControllers(); 44 45 app.Run();(4)、要实现授权的Controller增加【Authorize】特性,红色部分要注意。
using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; namespace PatrickLiu.Net6API.Resouces.Controllers { [Route("api/[controller]")] [ApiController] public class SecondController : ControllerBase { /// <summary> /// 这里使用 JWT 进行授权检查。 /// </summary> /// <returns></returns> [HttpGet] [Authorize(AuthenticationSchemes =JwtBearerDefaults.AuthenticationScheme)] public object GetData() { return new { Id = 1234, Name = "PatrickLiu" }; } } }(5)、appsettings.json 配置文件,这里的配置要和【PatrickLiu.Net6API.AuthenticationCenter】项目配置一样,红色部分要注意。
{ "Logging": { "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Warning" } }, "AllowedHosts": "*", "JWTTokenOption": { "Audience": "PatrickLiu.com", "Issuer": "PatrickLiu.com", "SecurityKey": "12333456677655ffrrffff" } }4、现在是Minimal WebAPI类型项目:
Microsoft.AspNetCore.Authentication.JwtBearer Microsoft.IdentityModel.Tokens(3)、Program.cs 源码如下,红色标注要特别重要。
using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.IdentityModel.Tokens; using PatrickLiu.Net6API.Extensions; using PatrickLiu.Net6API.MinimalAPI.Extension; using System.Text; var builder = WebApplication.CreateBuilder(args); builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); #region 1、配置鉴权逻辑 // 堆代码 duidaima.com //增加的鉴权逻辑,角色认证、策略认证都是支持的,和Net Core MVC 支持的一样。 JWTTokenOption tokenOption = new JWTTokenOption(); builder.Configuration.Bind("JWTTokenOption", tokenOption); builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(option => { option.TokenValidationParameters = new TokenValidationParameters() { ValidateIssuer = true,//是否验证 Issuer(发行商) ValidateAudience = true,//是否验证 Audience(受众者) ValidateLifetime = true,//是否验证失效时间 ValidateIssuerSigningKey = true,//是否验证 Issuer 的签名键 ValidAudience = tokenOption.Audience, ValidIssuer = tokenOption.Issuer,// ValidAudience,ValidIssuer这两项的值要和验证中心的只保持一致。 IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(tokenOption.SecurityKey!)) }; }); #endregion #region 2、配置授权逻辑 //在这里不支持增加授权信息,比如:builder.Services.AddAuthorization(JwtBearerDefaults.AuthenticationScheme);,这样写是不行的, 我们可以扩展实现 IAuthorizeData 来达到同样的目的。 builder.Services.AddAuthorization(); #endregion var app = builder.Build(); app.UseSwagger(); app.UseSwaggerUI(); #region 3、使用鉴权授权中间件 app.UseAuthentication(); app.UseAuthorization(); #endregion #region 4、实现授权要求 app.MapGet("/User", (int id, string name) => { return "Hello World!"; }).RequireAuthorization(new CustomAuthorizeData() { AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme //可以增加角色授权 //,Roles="", //可以增加策略授权。 //Policy="" }); app.MapGet("/Products", (HttpContext context) => { return new { Id = 123456, Name = "IPhone14 Pro Max", Price = 4839.23, DateOfProduction = "2023/2/12 23:22" }; }); #endregion app.Run();(4)、扩展 IAuthorizeData 接口实现CustomAuthorizeData,可以实现对MinimalAPI授权。
using Microsoft.AspNetCore.Authorization; namespace PatrickLiu.Net6API.MinimalAPI.Extension { public sealed class CustomAuthorizeData : IAuthorizeData { public string? Policy { get; set; } public string? Roles { get; set; } public string? AuthenticationSchemes { get; set; } } }(5)、appsettings.json 配置文件源码如下,红色部分是重要代码。
{ "Logging": { "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Warning" } }, "AllowedHosts": "*", "JWTTokenOption": { "Audience": "PatrickLiu.com", "Issuer": "PatrickLiu.com", "SecurityKey": "12333456677655ffrrffff" } }三、结束语