三种:基于角色,基于声明, 基于策略
此项目的demo的git地址:https://github.com/xeekseven/AspNet-core-Example/tree/master/ANC-Authorize-CustomRequirement
如果有这样的一个需求,一个管理系统,里面有多重角色,每个角色有多种权限,而且角色的权限是动态可调整了,一个用户多种角色,角色也是可调整了,这样一个: 权限(n)—(1) 角色 (n)— (1)用户 这样的一个需求,应该怎么实现?其实 ,自定义策略模式即可满足
为何不使用简单策略?简单策略模式无非就是逻辑运算符比基于声明多了些,但是比较鸡肋,不如......直接上自定义策略
复杂类型的授权,好处在于动态调整,当然就要自己写策略提供器,也就是根据不同的参数来生成不同的策略,然后还需要自己重新实现策略的handler
public class PermissionRequirement : IAuthorizationRequirement { public string PermissionName { get; } public PermissionRequirement (string PermissionName) { this.PermissionName = PermissionName; } }
//策略处理类 public class PermissionRequirementHandler : AuthorizationHandler<PermissionRequirement> { protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionRequirement requirement) { var role = context.User.FindFirst(c => c.Type == ClaimTypes.Role); if (role != null) { var roleValue = role.Value; var permissions = RolePermissionCache.GetPermissions(role.Value); if (permissions.Contains(requirement.PermissionName)) { context.Succeed(requirement); } } return Task.CompletedTask; } } //权限动态缓存类 临时替代数据库 public class RolePermissionCache { //实际在数据库获取与配置 public static List<string> GetPermissions(string role){ switch(role){ case "Administrator": return new List<string>(){ "Index","Privacy" }; case "Custom": return new List<string>(){ "Index" }; } return new List<string>(); } }
internal class PermissionPolicyProvider : IAuthorizationPolicyProvider { const string POLICY_PREFIX = "Permission"; public Task<AuthorizationPolicy> GetDefaultPolicyAsync() { return Task.FromResult(new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build()); } public Task<AuthorizationPolicy> GetFallbackPolicyAsync() { return Task.FromResult(new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build()); } public Task<AuthorizationPolicy> GetPolicyAsync(string policyName) { if(policyName.StartsWith(POLICY_PREFIX,System.StringComparison.OrdinalIgnoreCase)){ var policy = new AuthorizationPolicyBuilder(); policy.AddRequirements(new PermissionRequirement(policyName.Substring(POLICY_PREFIX.Length))); return Task.FromResult(policy.Build()); } return Task.FromResult<AuthorizationPolicy>(null); } }
internal class PermissionAuthorizeAttribute : AuthorizeAttribute { const string POLICY_PREFIX = "Permission"; public PermissionAuthorizeAttribute(string permissionName) => PermissionName = permissionName; public string PermissionName { get { return PermissionName; } set { Policy = $"{POLICY_PREFIX}{value.ToString()}"; } } }
public void ConfigureServices(IServiceCollection services) { services.Configure<CookiePolicyOptions>(options => { options.CheckConsentNeeded = context => true; options.MinimumSameSitePolicy = SameSiteMode.None; }); services.AddSingleton<IAuthorizationPolicyProvider, PermissionPolicyProvider>(); services.AddSingleton<IAuthorizationHandler, PermissionRequirementHandler>(); services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) .AddCookie(options => { options.AccessDeniedPath = new PathString("/Home/NotPermission"); options.LoginPath = new PathString("/Home/Login"); options.ExpireTimeSpan = TimeSpan.FromSeconds(10); }); services.AddControllersWithViews(); services.AddRazorPages(); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/Home/Error"); } app.UseStaticFiles(); app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); app.UseCookiePolicy(); app.UseEndpoints(endpoints => { endpoints.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); endpoints.MapRazorPages(); }); }
[PermissionAuthorize("Privacy")] public IActionResult Privacy() { return View(); }
[HttpPost] [AllowAnonymous] public async Task<IActionResult> Login(string username,string password) { var returnUrl = HttpContext.Request.Query["ReturnUrl"]; string roleType = ""; if(username == "admin"){ roleType = "Administrator"; } else if(username == "custom"){ roleType = "Custom"; } if((username == "admin" && password == "admin") || (username == "custom" && password == "custom")){ var claims = new List<Claim> { new Claim(ClaimTypes.Name,username), new Claim("Role",roleType), new Claim(ClaimTypes.Role,roleType) }; var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme); await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(claimsIdentity), new AuthenticationProperties()); if (!string.IsNullOrWhiteSpace(returnUrl)) { return Redirect(returnUrl); } return Redirect("/Home/Index"); } if (!string.IsNullOrWhiteSpace(returnUrl)) { return Redirect(returnUrl); } return Redirect("/Home/Login"); }
前一篇 —ASP.NET Core中的授权(2) — 基于声明: https://www.jianshu.com/p/f96c181c34d9