Asp.Net Core -Authorizationz授权
授权的内部实现参考
动态授权参考
动态授权
基于权限的授权
1.定义权限-Permissions
public class Permissions { public const string Admin = "Admin"; public const string Users = "Users"; public const string UserCreate = Users+ ".Create"; public const string UserUpdate = Users+ ".Update"; public const string UserDelete = Users+ ".Delete"; }
2.定义权限Requirement
public class PermissionAuthorizationRequirement : IAuthorizationRequirement { //表示权限的名称与Permission中常量对应 public string Name { get; set; } public PermissionAuthorizationRequirement(string name) { Name = name; } }
3.自定义授权Handler
public class PermissionAuthorizationHandler : AuthorizationHandler<PermissionAuthorizationRequirement> { protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionAuthorizationRequirement requirement) { //判断用户是否有某个角色 if (context.User.IsInRole(Permissions.Admin)) { context.Succeed(requirement); } else { //取出登录用户中ClaimType为Permission的项取出,并获取其 Value 组成一个 List<string> //var permissions = context.User.Claims.Where(p => p.Type == "Permissions").Select(p => p.Value).ToList(); var permissions = context.User.Claims.Where(p => p.Type == nameof(Permissions)).Select(p => p.Value).ToList(); //验证requirement是否满足授权(验证用户权限中是否存在requirement<请求中需要的权限>中的权限) if (permissions.Any(p => p.StartsWith(requirement.Name))) { //表示将验证成功的requirement从待验证的Requirements中移除(具体内容查看AuthorizationHandlerContext源码) context.Succeed(requirement); } } return Task.CompletedTask; } }
4.注册Handler
在Program中注册
builder.Services.AddSingleton<IAuthorizationHandler, PermissionAuthorizationHandler>();
5.根据定义的权限添加权限策略
手动添加权限策略
builder.Services.AddAuthorization(options => { options.AddPolicy(Permissions.Users, policy => policy.AddRequirements(new PermissionAuthorizationRequirement(Permissions.Users))); options.AddPolicy(Permissions.UserCreate, policy => policy.AddRequirements(new PermissionAuthorizationRequirement(Permissions.UserCreate))); options.AddPolicy(Permissions.UserUpdate, policy => policy.AddRequirements(new PermissionAuthorizationRequirement(Permissions.UserUpdate))); options.AddPolicy(Permissions.UserDelete, policy => policy.AddRequirements(new PermissionAuthorizationRequirement(Permissions.UserDelete))); });
自动添加权限策略(根据特性[Authorize]加入的权限动态添加)
/// <summary> /// 动态添加policy---自定义的 AuthorizationPolicyProvider 必须实现 IAuthorizationPolicyProvider,否则添加到 DI 时会不生效 /// 访问接口时需要用到权限就依次动态加载进来成为一项policy,由多少个权限就生成多少个policy,并且每一个policy都会按顺序执行一次 /// PermissionAuthorizationHandler的HandleRequirementAsync方法 /// </summary> public class PermissionAuthorizationPolicyProvider : DefaultAuthorizationPolicyProvider, IAuthorizationPolicyProvider { public PermissionAuthorizationPolicyProvider(IOptions<AuthorizationOptions> options) : base(options) { } //返回默认授权策略(在未指定策略的情况下用于 [Authorize] 属性的策略) public new Task<AuthorizationPolicy> GetDefaultPolicyAsync() => base.GetDefaultPolicyAsync(); //返回回退授权策略(在未指定策略时由授权中间件使用的策略) public new Task<AuthorizationPolicy?> GetFallbackPolicyAsync() => base.GetFallbackPolicyAsync(); //返回指定名称的授权策略 public new Task<AuthorizationPolicy?> GetPolicyAsync(string policyName) { if (policyName.StartsWith(Permissions.Users)) { var policy = new AuthorizationPolicyBuilder(JwtBearerDefaults.AuthenticationScheme); policy.AddRequirements(new PermissionAuthorizationRequirement(policyName)); return Task.FromResult<AuthorizationPolicy?>(policy.Build()); } return base.GetPolicyAsync(policyName); } }
将PermissionAuthorizationPolicyProvider进行DI注入
builder.Services.AddSingleton<IAuthorizationPolicyProvider, PermissionAuthorizationPolicyProvider>();
6.使用授权策略
以上方式需要对每个策略去做添加过于繁琐,可以通过MVC过滤器调用IAuthorizationService来简化操作
7.自定义过滤器-PermissionAuthorizeAttribute
[AttributeUsage(AttributeTargets.Class|AttributeTargets.Method,AllowMultiple =true,Inherited =true)] public class PermissionAuthorizeAttribute : Attribute, IAsyncAuthorizationFilter { public string Name { get; set; } public PermissionAuthorizeAttribute(string name) { Name = name; } public async Task OnAuthorizationAsync(AuthorizationFilterContext context) { var authorizationService=context.HttpContext.RequestServices.GetRequiredService<IAuthorizationService>(); //调用AuthorizeAsync方法 var authorizationResult = await authorizationService.AuthorizeAsync(context.HttpContext.User, null, new PermissionAuthorizationRequirement(Name)); if (!authorizationResult.Succeeded) { context.Result = new ForbidResult(); } } }
使用授权策略改为
ASP.NET Core 中内置了一些常用授权策略实现
- AssertionRequirement :使用最原始的断言形式来声明授权策略。
- DenyAnonymousAuthorizationRequirement :用于表示禁止匿名用户访问的授权策略,并在AuthorizationOptions中将其设置为默认策略。
- ClaimsAuthorizationRequirement :用于表示判断Cliams中是否包含预期的Claims的授权策略。
- RolesAuthorizationRequirement :用于表示使用ClaimsPrincipal.IsInRole来判断是否包含预期的Role的授权策略。
- NameAuthorizationRequirement:用于表示使用ClaimsPrincipal.Identities.Name来判断是否包含预期的Name的授权策略。
- OperationAuthorizationRequirement:用于表示基于操作的授权策略。
授权回退策略
应用于未显式指定授权策略的所有请求。 对于终结点路由提供的请求,这包括未指定授权属性的任何终结点。 对于在授权中间件之后由其他中间件提供的请求(如静态文件),这会将策略应用于所有请求。
builder.Services.AddAuthorization(options => { //具有授权属性的 Razor Pages、控制器或操作方法除外不受回退授权策略的影响,例如含有授权属性[AllowAnonymous] [Authorize]等的不受回退策略影响 //回退策略 回退授权策略要求所有用户进行身份验证 options.FallbackPolicy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build();//回退策略要求所有用户进行身份验证 });