设计本网站是用什么做的,网络营销专业背景,wordpress课程表单,东阳做网站一、概述ASP.NET Core MVC 提供了基于角色( Role )、声明( Chaim ) 和策略 ( Policy ) 等的授权方式。在实际应用中#xff0c;可能采用部门#xff08; Department , 本文采用用户组 Group #xff09;、职位 ( 可继续沿用 Role )、权限( Permission )的方式进行授权。要达… 一、概述ASP.NET Core MVC 提供了基于角色( Role )、声明( Chaim ) 和策略 ( Policy ) 等的授权方式。在实际应用中可能采用部门 Department , 本文采用用户组 Group 、职位 ( 可继续沿用 Role )、权限( Permission )的方式进行授权。要达到这个目的仅仅通过自定义 IAuthorizationPolicyProvider 是不行的。本文通过自定义 IApplicationModelProvide 进行扩展。二、PermissionAuthorizeAttribute : IPermissionAuthorizeDataAuthorizeAttribute 类实现了 IAuthorizeData 接口namespace Microsoft.AspNetCore.Authorization{ /// summary /// Defines the set of data required to apply authorization rules to a resource. /// /summary public interface IAuthorizeData { /// summary /// Gets or sets the policy name that determines access to the resource. /// /summary string Policy { get; set; } /// summary /// Gets or sets a comma delimited list of roles that are allowed to access the resource. /// /summary string Roles { get; set; } /// summary /// Gets or sets a comma delimited list of schemes from which user information is constructed. /// /summary string AuthenticationSchemes { get; set; } }}使用 AuthorizeAttribute 不外乎如下几种形式[Authorize][Authorize(SomePolicy)][Authorize(Roles 角色1,角色2)][Authorize(AuthenticationSchemes JwtBearerDefaults.AuthenticationScheme)]当然参数还可以组合起来。另外Roles 和 AuthenticationSchemes 的值以半角逗号分隔是 Or 的关系多个 Authorize 是 And 的关系Policy 、Roles 和 AuthenticationSchemes 如果同时使用也是 And 的关系。如果要扩展 AuthorizeAttribute先扩展 IAuthorizeData 增加新的属性public interface IPermissionAuthorizeData : IAuthorizeData{ string Groups { get; set; } string Permissions { get; set; }}然后定义 AuthorizeAttribute:[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple true, Inherited true)]public class PermissionAuthorizeAttribute : Attribute, IPermissionAuthorizeData{ public string Policy { get; set; } public string Roles { get; set; } public string AuthenticationSchemes { get; set; } public string Groups { get; set; } public string Permissions { get; set; }}现在在 Controller 或 Action 上就可以这样使用了[PermissionAuthorize(Roles 经理,副经理)] // 经理或部门经理[PermissionAuthorize(Groups 研发部,生产部, Roles 经理] // 研发部经理或生成部经理。Groups 和 Roles 是 And 的关系。[PermissionAuthorize(Groups 研发部,生产部, Roles 经理, Permissions 请假审批] // 研发部经理或生成部经理并且有请假审批的权限。Groups 、Roles 和 Permission 是 And 的关系。数据已经准备好下一步就是怎么提取出来。通过扩展 AuthorizationApplicationModelProvider 来实现。三、PermissionAuthorizationApplicationModelProvider : IApplicationModelProviderAuthorizationApplicationModelProvider 类的作用是构造 AuthorizeFilter 对象放入 ControllerModel 或 ActionModel 的 Filters 属性中。具体过程是先提取 Controller 和 Action 实现了 IAuthorizeData 接口的 Attribute如果使用的是默认的DefaultAuthorizationPolicyProvider则会先创建一个 AuthorizationPolicy 对象作为 AuthorizeFilter 构造函数的参数。创建 AuthorizationPolicy 对象是由 AuthorizationPolicy 的静态方法 public static async TaskAuthorizationPolicy CombineAsync(IAuthorizationPolicyProvider policyProvider, IEnumerableIAuthorizeData authorizeData) 来完成的。该静态方法会解析 IAuthorizeData 的数据但不懂解析 IPermissionAuthorizeData。因为 AuthorizationApplicationModelProvider 类对 AuthorizationPolicy.CombineAsync 静态方法有依赖这里不得不做一个类似的 PermissionAuthorizationApplicationModelProvider 类在本类实现 CombineAsync 方法。暂且不论该方法放在本类是否合适的问题。 public static AuthorizeFilter GetFilter(IAuthorizationPolicyProvider policyProvider, IEnumerableIAuthorizeData authData) { // The default policy provider will make the same policy for given input, so make it only once. // This will always execute synchronously. if (policyProvider.GetType() typeof(DefaultAuthorizationPolicyProvider)) { var policy CombineAsync(policyProvider, authData).GetAwaiter().GetResult(); return new AuthorizeFilter(policy); } else { return new AuthorizeFilter(policyProvider, authData); } } private static async TaskAuthorizationPolicy CombineAsync(IAuthorizationPolicyProvider policyProvider, IEnumerableIAuthorizeData authorizeData) { if (policyProvider null) { throw new ArgumentNullException(nameof(policyProvider)); } if (authorizeData null) { throw new ArgumentNullException(nameof(authorizeData)); } var policyBuilder new AuthorizationPolicyBuilder(); var any false; foreach (var authorizeDatum in authorizeData) { any true; var useDefaultPolicy true; if (!string.IsNullOrWhiteSpace(authorizeDatum.Policy)) { var policy await policyProvider.GetPolicyAsync(authorizeDatum.Policy); if (policy null) { //throw new InvalidOperationException(Resources.FormatException_AuthorizationPolicyNotFound(authorizeDatum.Policy)); throw new InvalidOperationException(nameof(authorizeDatum.Policy)); }policyBuilder.Combine(policy); useDefaultPolicy false; } var rolesSplit authorizeDatum.Roles?.Split(,); if (rolesSplit ! null rolesSplit.Any()) { var trimmedRolesSplit rolesSplit.Where(r !string.IsNullOrWhiteSpace(r)).Select(r r.Trim()); policyBuilder.RequireRole(trimmedRolesSplit); useDefaultPolicy false; } if(authorizeDatum is IPermissionAuthorizeData permissionAuthorizeDatum ) { var groupsSplit permissionAuthorizeDatum.Groups?.Split(,); if (groupsSplit ! null groupsSplit.Any()) { var trimmedGroupsSplit groupsSplit.Where(r !string.IsNullOrWhiteSpace(r)).Select(r r.Trim()); policyBuilder.RequireClaim(Group, trimmedGroupsSplit); // TODO: 注意硬编码 useDefaultPolicy false; } var permissionsSplit permissionAuthorizeDatum.Permissions?.Split(,); if (permissionsSplit ! null permissionsSplit.Any()) { var trimmedPermissionsSplit permissionsSplit.Where(r !string.IsNullOrWhiteSpace(r)).Select(r r.Trim()); policyBuilder.RequireClaim(Permission, trimmedPermissionsSplit);// TODO: 注意硬编码 useDefaultPolicy false; } } var authTypesSplit authorizeDatum.AuthenticationSchemes?.Split(,); if (authTypesSplit ! null authTypesSplit.Any()) { foreach (var authType in authTypesSplit) { if (!string.IsNullOrWhiteSpace(authType)) { policyBuilder.AuthenticationSchemes.Add(authType.Trim()); } } } if (useDefaultPolicy) {policyBuilder.Combine(await policyProvider.GetDefaultPolicyAsync()); } } return any ? policyBuilder.Build() : null; }if(authorizeDatum is IPermissionAuthorizeData permissionAuthorizeDatum ) 为扩展部分。四、Startup注册 PermissionAuthorizationApplicationModelProvider 服务需要在 AddMvc 之后替换掉 AuthorizationApplicationModelProvider 服务。services.AddMvc();services.Replac(ServiceDescriptor.TransientIApplicationModelProvider,PermissionAuthorizationApplicationModelProvider());五、Jwt 示例[Route(api/[controller])][ApiController]public class ValuesController : ControllerBase{ private readonly JwtSecurityTokenHandler _tokenHandler new JwtSecurityTokenHandler(); [HttpGet] [Route(SignIn)] public async TaskActionResultstring SignIn() { var user new ClaimsPrincipal(new ClaimsIdentity(new[] { // 备注Claim Type: Group 和 Permission 这里使用的是硬编码应该定义为类似于 ClaimTypes.Role 的常量另外下列模拟数据不一定合逻辑。 new Claim(ClaimTypes.Name, Bob), new Claim(ClaimTypes.Role, 经理), // 注意不能使用逗号分隔来达到多个角色的目的下同。 new Claim(ClaimTypes.Role, 副经理), new Claim(Group, 研发部), new Claim(Group, 生产部), new Claim(Permission, 请假审批), new Claim(Permission, 权限1), new Claim(Permission, 权限2), }, JwtBearerDefaults.AuthenticationScheme)); var token new JwtSecurityToken( SignalRAuthenticationSample, SignalRAuthenticationSample, user.Claims, expires: DateTime.UtcNow.AddDays(30), signingCredentials: SignatureHelper.GenerateSigningCredentials(1234567890123456)); return _tokenHandler.WriteToken(token); } [HttpGet] [Route(Test)] [PermissionAuthorize(Groups 研发部,生产部, Roles 经理, Permissions 请假审批] // 研发部经理或生成部经理并且有请假审批的权限。Groups 、Roles 和 Permission 是 And 的关系。 public async TaskActionResultIEnumerablestring Test() { var user HttpContext.User; return new string[] { value1, value2 }; } }六、问题AuthorizeFilter 类显示实现了 IFilterFactory 接口的 CreateInstance 方法IFilterMetadata IFilterFactory.CreateInstance(IServiceProvider serviceProvider){ if (Policy ! null || PolicyProvider ! null) { // The filter is fully constructed. Use the current instance to authorize. return this; } Debug.Assert(AuthorizeData ! null); var policyProvider serviceProvider.GetRequiredServiceIAuthorizationPolicyProvider(); return AuthorizationApplicationModelProvider.GetFilter(policyProvider, AuthorizeData);}竟然对 AuthorizationApplicationModelProvider.GetFilter 静态方法产生了依赖。庆幸的是如果通过 AuthorizeFilter(IAuthorizationPolicyProvider policyProvider, IEnumerableIAuthorizeData authorizeData) 或 AuthorizeFilter(AuthorizationPolicy policy) 创建 AuthorizeFilter 对象不会产生什么不良影响。七、下一步[PermissionAuthorize(Groups 研发部,生产部, Roles 经理, Permissions 请假审批] 这种形式还是不够灵活哪怕用多个 Attribute, And 和 Or 的逻辑组合不一定能满足需求。可以在 IPermissionAuthorizeData 新增一个 Rule 属性实现类似的效果[PermissionAuthorize(Rule (Groups:研发部,生产部)(Roles:请假审批||Permissions:超级权限)]通过 Rule 计算复杂的授权。八、如果通过自定义 IAuthorizationPolicyProvider 实现另一种方式是自定义 IAuthorizationPolicyProvider 不过还需要自定义 AuthorizeFilter。因为当不是使用 DefaultAuthorizationPolicyProvider 而是自定义 IAuthorizationPolicyProvider 时AuthorizationApplicationModelProvider或前文定义的 PermissionAuthorizationApplicationModelProvider会使用 AuthorizeFilter(IAuthorizationPolicyProvider policyProvider, IEnumerableIAuthorizeData authorizeData) 创建 AuthorizeFilter 对象而不是 AuthorizeFilter(AuthorizationPolicy policy)。这会造成 AuthorizeFilter 对象在 OnAuthorizationAsync 时会间接调用 AuthorizationPolicy.CombineAsync 静态方法。这可以说是一个设计上的缺陷不应该让 AuthorizationPolicy.CombineAsync 静态方法存在哪怕提供个 IAuthorizationPolicyCombiner 也好。另外上文提到的 AuthorizationApplicationModelProvider.GetFilter 静态方法同样不是一种好的设计。等微软想通吧。参考资料https://docs.microsoft.com/zh-cn/aspnet/core/security/authorization/iauthorizationpolicyprovider?viewaspnetcore-2.1 排版问题http://blog.tubumu.com/2018/11/28/aspnetcore-mvc-extend-authorization/