1.5 基于策略的授权
在上篇中,已经讲到了授权访问(authorization)的四种方式。其中Razor Pages授权约定和简单授权二种方式更像是身份认证(authentication) ,因为只要是合法用户登录就能访问资源。 而角色授权和声明授权二种方式是真正的授权访问(authorization)。
下面继续讲authorization的第五种方式--策略授权。策略授权由一个或多个需求(也可以称"要求")组成(需求:TRequirement)。它在程序启动时注册为授权服务配置的一部分。在ConfigureServices方法中注册。
(1) 注册策略授权
创建了一个名为"AtLeast21"的策略授权,这个策略的需求是最小年龄需求,策略通过参数对象(IAuthorizationRequirement)提供,它要求最低年龄是21岁。
public void ConfigureServices(IServiceCollection services) { services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2); services.AddAuthorization(options => { options.AddPolicy("EmployeeOnly", policy => policy.RequireClaim("EmployeeNumber")); // MinimumAgeRequirement参数对象 实现了IAuthorizationRequirement options.AddPolicy("AtLeast21", policy => policy.Requirements.Add(new MinimumAgeRequirement(21))); }); }
(2) 策略授权应用到mvc的控制器或Razor Pages
//用户购买酒业务, 策略授权应用到控制器,要求用户年龄不能低于21岁 [Authorize(Policy = "AtLeast21")] public class AlcoholPurchaseController : Controller { public IActionResult Index() => View(); }
//策略授权到razor pages的PageModel类 [Authorize(Policy = "AtLeast21")] public class AlcoholPurchaseModel : PageModel { }
在razor pages中,策略还可以应用到razor page授权约定中。
public static PageConventionCollection AuthorizeFolder(this PageConventionCollection conventions, string folderPath, string policy);
(3) Requirement策略授权需求
策略授权需求实现IAuthorizationRequirement接口,用于策略需求对象参数传递。MinimumAgeRequirement就是一个需求参数对象。
using Microsoft.AspNetCore.Authorization; public class MinimumAgeRequirement : IAuthorizationRequirement { public int MinimumAge { get; } public MinimumAgeRequirement(int minimumAge) { MinimumAge = minimumAge; } }
(4) 策略授权处理程序类
授权处理程序负责评估要求的属性(指策略授权逻辑处理,把当前用户的年龄与策略要求年龄进行验证)。 授权处理程序会针对提供的AuthorizationHandlerContext 来评估要求,确定是否允许访问或拒绝。
实现策略授权处理程序,需要继承AuthorizationHandler<TRequirement>,其中TRequirement就是参数对象。另外,一个处理程序也可以通过实现 IAuthorizationHandler 来处理多个类型的要求。
下面是一对一关系的示例(一个Handler处理一个TRequirement对象),评估最低年龄要求:
public class MinimumAgeHandler: AuthorizationHandler<MinimumAgeRequirement> { protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, MinimumAgeRequirement requirement) { if (!context.User.HasClaim(c => c.Type == ClaimTypes.DateOfBirth && c.Issuer == "LOCAL AUTHORITY")) { //TODO: Use the following if targeting a version of //.NET Framework older than 4.6: // return Task.FromResult(0); return Task.CompletedTask; } var dateOfBirth = Convert.ToDateTime( context.User.FindFirst(c => c.Type == ClaimTypes.DateOfBirth && c.Issuer == "LOCAL AUTHORITY").Value); int calculatedAge = DateTime.Today.Year - dateOfBirth.Year; if (dateOfBirth > DateTime.Today.AddYears(-calculatedAge)) { calculatedAge--; } if (calculatedAge >= requirement.MinimumAge) { //满足的要求作为其唯一参数 context.Succeed(requirement); } //TODO: Use the following if targeting a version of //.NET Framework older than 4.6: // return Task.FromResult(0); return Task.CompletedTas