目录后端
学有所得架构
通过前篇的介绍,你们对jwt应该有很清晰的认识了,接下来咱们如何让代码更清晰,以便之后面的扩展和对比;ide
首先,咱们先把一篇的自定义策略验证进行封装,放到一个静态扩展方法中去,和上一节仍是有点区别,多加了一点东西,以下:ui
public static class ConfigServiceExtension { public static void AddInnerAuthorize(this IServiceCollection services, IConfiguration config) { services.AddAuthorization(option => { //自定义一些策略,原理都是基于申明key和value的值进行比较或者是否有无 #region 键值对对比的一些验证策略 option.AddPolicy("onlyRober", policy => policy.RequireClaim("name", "rober")); option.AddPolicy("onlyAdminOrSuperUser", policy => policy.RequireClaim(ClaimTypes.Role, "Admin", "SuperUser")); //多申明共同,申明中包含aud:rober或者申明中值有等于Rober的均可以经过 option.AddPolicy("multiClaim", policy => policy.RequireAssertion(context => { return context.User.HasClaim("aud", config["JwtOption:Audience"]) || context.User.HasClaim(c => c.Value == config["JwtOption:Name"]); })); #endregion #region 自定义验证策略 option.AddPolicy("ageRequire", policy => policy.Requirements.Add(new AgeRequireMent(20))); option.AddPolicy("common", policy => policy.Requirements.Add(new CommonAuthorize())); #endregion }).AddAuthentication(option => { option.DefaultScheme = JwtBearerDefaults.AuthenticationScheme; }).AddJwtBearer(option => { if (!string.IsNullOrEmpty(config["JwtOption:SecurityKey"])) { TokenContext.securityKey = config["JwtOption:SecurityKey"]; } //设置须要验证的项目 option.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true,//是否验证Issuer ValidateAudience = true,//是否验证Audience ValidateLifetime = true,//是否验证失效时间 ValidateIssuerSigningKey = true,//是否验证SecurityKey ValidAudience = config["JwtOption:Audience"],//Audience ValidIssuer = config["JwtOption:Issuer"],//Issuer,这两项和前面签发jwt的设置一致 IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(TokenContext.securityKey))//拿到SecurityKey }; }); //自定义策略IOC添加 services.AddSingleton<IAuthorizationHandler, AgeRequireHandler>(); services.AddSingleton<IAuthorizationHandler, CommonAuthorizeHandler>(); } } /// <summary> /// jwt配置项目(从配置文件中初始化) /// </summary> public class JwtOption { public string Issuer { get; set; } = "rober"; public string Audience { get; set; } = "rober"; public TimeSpan Expiration { get; set; } = TimeSpan.FromMinutes(50); public string SecurityKey { get; set; } = "www.rober.com(welcome you)"; public string Name { get; set; } }
第1、在之前的基础上添加了几个简单的自定义策略(onlyRober,onlyAdminOrSuperUser,multiClaim),this
第2、还有个自定义策略AgeRequireMent,这个和上一篇讲的相似,也是两个重要的类组成(AgeRequireHandler 和 AgeRequireMent )请自行参考代码,以下:spa
/// <summary> /// 访问者年龄要求自定义策略 /// </summary> public class AgeRequireHandler : AuthorizationHandler<AgeRequireMent> { protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, AgeRequireMent requirement) { if (!context.User.HasClaim(c => c.Type == "age")) { return Task.CompletedTask; } int.TryParse(context.User.Claims.FirstOrDefault(c => c.Type == "age").Value, out int age); if (age >= requirement.Age) { context.Succeed(requirement);//标识验证成功 } return Task.CompletedTask; } } public class AgeRequireMent : IAuthorizationRequirement { public int Age { get; set; } public AgeRequireMent(int age) => this.Age = age; }
第3、AddJwtBearer方法中添加了验证签名正确的验证项目code
ValidateIssuer = true,//是否验证Issuer,设置值为 ValidIssuer = config["JwtOption:Issuer"],//Issuer,这两项和前面签发jwt的设置一致
ValidateAudience = true,//是否验证Audience ,设置值 ValidAudience = config["JwtOption:Audience"],//
ValidateLifetime = true,//是否验证失效时间
ValidateIssuerSigningKey = true,//是否验证SecurityKeyjwt
第四,把自定义策略,注入进去
blog
其次,咱们如何使用上面定义了那么多策略呢?经过[Authorize(Policy = "策略名称")]放在controller或者action上面rem
代码以下:
/// <summary> /// 权限示例控制器, /// </summary> public class RAuthorizeDemoController : BaseController { private readonly JwtOption option; public RAuthorizeDemoController(IOptions<JwtOption> optionContainer) { this.option = optionContainer.Value; } /// <summary> /// 基本的简单验证 /// </summary> /// <returns></returns> [HttpGet] [Authorize] public ReturnMsg Get() { return ReturnMsg.Get(true, "Success"); } /// <summary> /// 基于某一策略受权 /// </summary> /// <returns></returns> [HttpGet] [Authorize(Policy = "onlyRober")] public ReturnMsg GetUser() { return ReturnMsg.Get(true); } /// <summary> /// 基于多策略受权 /// </summary> /// <returns></returns> [HttpGet] [Authorize(Policy = "multiClaim")] public ReturnMsg GetMulti() { return ReturnMsg.Get(true); } /// <summary> /// 基于某一角色受权 /// </summary> /// <returns></returns> [HttpGet] [Authorize(Roles = "Admin,SuperUser")] public ReturnMsg GetRole() { return ReturnMsg.Get(true); } /// <summary> /// 基于 role策略受权和上面GetRole效果相同 /// </summary> /// <returns></returns> [Authorize(Policy = "onlyAdminOrSuperUser")] [HttpGet] public ReturnMsg GetRoleCopy() { return ReturnMsg.Get(true); } [Authorize(Policy = "ageRequire")] [HttpGet] public ReturnMsg GetAge() { return ReturnMsg.Get(true); } [Authorize(Policy = "common")] [HttpGet] public ReturnMsg GetCommon() { return ReturnMsg.Get(true); } }
看到这里使用内置的AuthorizeAttribute类进行各类策略验证是否是也很简单呢?
估计你们对各类策略的实质也有深入认识了,接下来咱们来作个对比,实际上它内部也就是那样的实现的
jwt自定义验证(请看上一节的自定义里面的验证方法中的代码) | 使用[Authorize(Policy = "策略名称")]验证,请参考上面的RAuthorizeDemoController 类 | 备注 | ||
success = success && payLoad["name"]?.ToString()=="rober" | option.AddPolicy("onlyRober", policy => policy.RequireClaim("name", "rober")); |
判断payLoad["name"]=="rober" | ||
success = success &&( payLoad["http://schemas.microsoft.com/ws/2008/06/identity/claims/role"]?.ToString()=="Admin" || payLoad["http://schemas.microsoft.com/ws/2008/06/identity/claims/role"]?.ToString()=="SuperUser") | option.AddPolicy("onlyAdminOrSuperUser", policy => policy.RequireClaim(ClaimTypes.Role, "Admin", "SuperUser")); |
|||
success = success &&( payLoad["aud"]?.ToString()=="roberAudience" || payLoad.Keys.Contains("name")) | option.AddPolicy("multiClaim", policy => policy.RequireAssertion(context => |
|||
上面都是等价的,其余类推,无非就是正对payLoad的值进行各类对比;
到这里jwt的验证也接近尾声了,是否是以为jwt验证很简单啊?
不要被各类写法给绕晕了,仔细看看它的原理,其实很是简单,无非就是对payload的各类值进行判断和jwt的有效性验证;
在项目中,其实用的最多的就是上一节的内自定义jwt管道验证和自定义策略“common”的验证方法,我不少项目中就是这么用的,至于用户,角色策略用的很是少;
接下来,我将发表一个aspnet core 2.1以上版本的后端架构方面的博文,这个架构但是实战中的架构(已经经历20多个项目,10多年的不断更新和删减),不是demo;
但愿经过简单易懂的原理入门,到深刻了解,再到轻松愉快运用的过程,最终但愿你们共同进步,共同加入,开源出去;