双端验证
在此项目中,User和Admin两端是分别发送了不同格式的Token以区分登陆状态
Admin: Token: <token>
User: Authorization:<token>
所以我们在配置JWT验证规则时,应该分别为其配置验证规则
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| .AddJwtBearer("Admin", options => { options.RequireHttpsMetadata = false; options.SaveToken = true; options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuerSigningKey = true, IssuerSigningKey = new SymmetricSecurityKey( Encoding.ASCII.GetBytes(builder.Configuration["Jwt:Key"] ?? throw new InvalidOperationException())),
ValidateIssuer = true, ValidIssuer = builder.Configuration["Jwt:Issuer"], ValidateAudience = true, ValidAudience = builder.Configuration["Jwt:Audience"] }; options.EventsType = typeof(AdminJwtBearerEvents); })
.AddJwtBearer("User", options => { options.RequireHttpsMetadata = false; options.SaveToken = true; options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuerSigningKey = true, IssuerSigningKey = new SymmetricSecurityKey( Encoding.ASCII.GetBytes(builder.Configuration["Jwt:Key"] ?? throw new InvalidOperationException())), ValidateIssuer = true, ValidIssuer = builder.Configuration["Jwt:Issuer"], ValidateAudience = true, ValidAudience = builder.Configuration["Jwt:Audience"] }; options.EventsType = typeof(UserJwtBearerEvents); });
|
JwtBearerEvents
分别添加Admin身份认证方案和User身份认证方案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| public class AdminJwtBearerEvents : JwtBearerEvents { public override Task MessageReceived(MessageReceivedContext context) { var token = context.Request.Headers["Token"].FirstOrDefault(); if (!string.IsNullOrEmpty(token)) { context.Token = token; } return base.MessageReceived(context); } }
public class UserJwtBearerEvents : JwtBearerEvents { public override Task MessageReceived(MessageReceivedContext context) { var token = context.Request.Headers["Authorization"].FirstOrDefault(); if (!string.IsNullOrEmpty(token)) { context.Token = token; } return base.MessageReceived(context); } }
|
AuthenticationSchemes
我们在Controller中,可以使用不同的身份认证方案来对不同的Action方法进行验证
1 2 3 4 5 6 7 8 9 10
|
[Authorize(AuthenticationSchemes = "Admin")] [HttpPost("logout")] public Result<string> Logout() { return Result<string>.Success(); }
|
我们也可以将这个方法直接添加到控制器类上,再将登陆方法暴露出来
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| [ApiController] [Route("/admin/employee")] [Authorize(AuthenticationSchemes = "Admin")] public class EmployeeController : ControllerBase { [HttpPost("login")] [AllowAnonymous] public Result<EmployeeLoginResp> Login([FromBody] EmployeeLoginReq employeeLoginReq) { }
|
Swagger按钮
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
| #region MyRegion Swagger
builder.Services.AddSwaggerGen( options => { options.AddSecurityDefinition("Admin", new OpenApiSecurityScheme { Description = "Token: <token>", Name = "Token", In = ParameterLocation.Header, Type = SecuritySchemeType.ApiKey, }); options.AddSecurityRequirement(new OpenApiSecurityRequirement { { new OpenApiSecurityScheme { Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "Admin" }, }, new List<string>() } });
options.AddSecurityDefinition("User", new OpenApiSecurityScheme { Description = "Authorization: <token>", Name = "Authorization", In = ParameterLocation.Header, Type = SecuritySchemeType.ApiKey, }); options.AddSecurityRequirement(new OpenApiSecurityRequirement { { new OpenApiSecurityScheme { Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "User" }, }, new List<string>() } }); });
#endregion
|

Swagger文档
我们应该为Admin和User配置不同的接口文档,以方便测试使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| builder.Services.AddSwaggerGen( options => { options.SwaggerDoc("admin", new OpenApiInfo { Title = "Admin API", Version = "v1" }); options.SwaggerDoc("user", new OpenApiInfo { Title = "User API", Version = "v1" }); options.DocInclusionPredicate((docName, apiDesc) => { if (apiDesc.ActionDescriptor is ControllerActionDescriptor actionDescriptor) { var controllerNamespace = actionDescriptor.ControllerTypeInfo.Namespace;
if (controllerNamespace != null && controllerNamespace.Contains("Controllers")) { if (docName == "user") { return controllerNamespace.Contains("User"); }
if (docName == "admin") { return controllerNamespace.Contains("Admin"); } } }
return false; }); }); )
|
配置中间件
1 2 3 4 5 6
| app.UseSwaggerUI(options => { options.SwaggerEndpoint("/swagger/v1-user/swagger.json", "User API V1"); options.SwaggerEndpoint("/swagger/v1-admin/swagger.json", "Admin API V1"); } );
|