• 权限模块和Feature模块


    基于策略的授权是授权的核心,基于角色的授权和基于Scheme的授权只是一种语法上的便捷,最终都会生成授权策略,使用基于策略的授权时,首先要定义授权策略,而授权策略本质上就是对Claims的一系列断言。

    1、先用IAuthorizationPolicyProvider根据名称得到AuthorizationPolicy,而AuthorizationPolicy对象包含IReadOnlyList<IAuthorizationRequirement> Requirements列表

    2、 每一个 Requirement 都需要有一个对应的 Handler(IAuthorizationHandler)来完成授权逻辑.

    3、IAuthorizationService的工作原理:首先根据策略名字,由IAuthorizationPolicyProvider得到AuthorizationPolicy,然后由require对应的Handler进行验证

      

     每一个Requirement都代表一个授权条件;比如DenyAnonymousAuthorizationRequirement 表示禁止匿名用户访问的授权策略;ClaimsAuthorizationRequirement 用于表示判断Cliams中是否包含预期的Claims的授权策略。

    ABP,定义一个代表操作的Requirement:

       public class PermissionRequirement : IAuthorizationRequirement
        {
            public string PermissionName { get; }
    
            public PermissionRequirement([NotNull]string permissionName)
            {
                Check.NotNull(permissionName, nameof(permissionName));
    
                PermissionName = permissionName;
            }
        }

     每一个 Requirement 都需要有一个对应的 Handler来完成授权逻辑,可以直接让 Requirement 实现IAuthorizationHandler接口,也可以单独定义授权Handler

    我们在实现IAuthorizationHandler接口时,通常是继承自AuthorizationHandler<TRequirement>来实现,ABP有如下定义:

    依赖于_permissionChecker进行验证

    public class PermissionRequirementHandler : AuthorizationHandler<PermissionRequirement>
        {
            private readonly IPermissionChecker _permissionChecker;
    
            public PermissionRequirementHandler(IPermissionChecker permissionChecker)
            {
                _permissionChecker = permissionChecker;
            }
    
            protected override async Task HandleRequirementAsync(
                AuthorizationHandlerContext context,
                PermissionRequirement requirement)
            {
                if (await _permissionChecker.IsGrantedAsync(context.User, requirement.PermissionName))
                {
                    context.Succeed(requirement);
                }
            }
        }

    授权策略具体表现为一个AuthorizationPolicy对象,要判断的是用户是否具有针对该资源的某项操作,

    AuthorizationPolicyBuilder,它提供了一系列创建AuthorizationPolicy的快捷方法。

    IAuthorizationPolicyProvider是根据名称获取到策略对象,默认实现为DefaultAuthorizationPolicyProvider,从AuthorizationOptions _options里面获取_options.GetPolicy(policyName)

    context.Services.AddAuthorization(options =>
                {
                    options.AddPolicy("MyClaimTestPolicy", policy =>
                    {
                        policy.RequireClaim("MyCustomClaimType", "42");
                    });
                });

    ABP自定义AbpAuthorizationPolicyProvider,派生DefaultAuthorizationPolicyProvider,在GetPolicyAsync方法里从IPermissionDefinitionManager得到permissiondefinition的定义,再利用AuthorizationPolicyBuilder创建AuthorizationPolicy对象

     public override async Task<AuthorizationPolicy> GetPolicyAsync(string policyName)
            {
                var policy = await base.GetPolicyAsync(policyName);
                if (policy != null)
                {
                    return policy;
                }
    
                var permission = _permissionDefinitionManager.GetOrNull(policyName);
                if (permission != null)
                {
                    //TODO: Optimize & Cache!
                    var policyBuilder = new AuthorizationPolicyBuilder(Array.Empty<string>());
                    policyBuilder.Requirements.Add(new PermissionRequirement(policyName));
                    return policyBuilder.Build();
                }
    
                return null;
            }

     权限是主要是通过调用IAuthorizationService来完成的,而授权策略的本质是提供 Requirement ,我们完全可以使用它们两个来完成各种灵活的授权方式,而不用局限于策略;在默认的AuthorizationHandlerProvider中,会从DI系统中获取到我们注册的所有Handler,最终调用其HandleAsync方法。;

    public class DefaultAuthorizationService : IAuthorizationService
    {
        private readonly AuthorizationOptions _options;
        private readonly IAuthorizationHandlerContextFactory _contextFactory;
        private readonly IAuthorizationHandlerProvider _handlers;
        private readonly IAuthorizationEvaluator _evaluator;
        private readonly IAuthorizationPolicyProvider _policyProvider;
    
        public async Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user, object resource, string policyName)
        {        
            var policy = await _policyProvider.GetPolicyAsync(policyName);
            return await this.AuthorizeAsync(user, resource, policy);
        }
    
        public async Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user, object resource, IEnumerable<IAuthorizationRequirement> requirements)
        {
            var authContext = _contextFactory.CreateContext(requirements, user, resource);
            var handlers = await _handlers.GetHandlersAsync(authContext);
            foreach (var handler in handlers)
            {
                await handler.HandleAsync(authContext);
                if (!_options.InvokeHandlersAfterFailure && authContext.HasFailed)
                {
                    break;
                }
            }
            return _evaluator.Evaluate(authContext);
        }
    }
    public abstract class AuthorizationHandler<TRequirement> : IAuthorizationHandler where TRequirement : IAuthorizationRequirement
    {
        public virtual async Task HandleAsync(AuthorizationHandlerContext context)
        {
            foreach (var req in context.Requirements.OfType<TRequirement>())
            {
                await HandleRequirementAsync(context, req);
            }
        }
    
        protected abstract Task HandleRequirementAsync(AuthorizationHandlerContext context, TRequirement requirement);
     public class PermissionRequirementHandler : AuthorizationHandler<PermissionRequirement>
        {
            private readonly IPermissionChecker _permissionChecker;
    
            public PermissionRequirementHandler(IPermissionChecker permissionChecker)
            {
                _permissionChecker = permissionChecker;
            }
    
            protected override async Task HandleRequirementAsync(
                AuthorizationHandlerContext context,
                PermissionRequirement requirement)
            {
                if (await _permissionChecker.IsGrantedAsync(context.User, requirement.PermissionName))
                {
                    context.Succeed(requirement);
                }
            }
        }

     

    一、模块的配置

    1、增加权限拦截器,只要类型和方法有AuthorizeAttribute都增加AuthorizationInterceptor

         public static void RegisterIfNeeded(IOnServiceRegistredContext context)
            {
                if (ShouldIntercept(context.ImplementationType))
                {
                    context.Interceptors.TryAdd<AuthorizationInterceptor>();
                }
            }
    
            private static bool ShouldIntercept(Type type)
            {
                return type.IsDefined(typeof(AuthorizeAttribute), true) ||
                       AnyMethodHasAuthorizeAttribute(type);
            }
    

    2、PermissionOptions权限的两个集合,权限的定义IPermissionDefinitionProvider,以及权限的提供者IPermissionValueProvider

    注册PermissionRequirementHandler到DI系统里面

     public override void PreConfigureServices(ServiceConfigurationContext context)
            {
                context.Services.OnRegistred(AuthorizationInterceptorRegistrar.RegisterIfNeeded);
                AutoAddDefinitionProviders(context.Services);
            }
    
            public override void ConfigureServices(ServiceConfigurationContext context)
            {
                context.Services.AddAuthorization();
    
                context.Services.AddSingleton<IAuthorizationHandler, PermissionRequirementHandler>();
    
                Configure<PermissionOptions>(options =>
                {
                    options.ValueProviders.Add<UserPermissionValueProvider>();
                    options.ValueProviders.Add<RolePermissionValueProvider>();
                    options.ValueProviders.Add<ClientPermissionValueProvider>();
                });
            }

    1)PermissionDefinition的定义有,名称,父定义Parent ,租户端(Tenant,Host,Both),允许providers(指的valueProvider) ,显示名字,子权限Children列表List<PermissionDefinition>,客户化属性定义Dictionary<string, object> Properties,WithProperty,WithProviders两个方法设置   

    2、PermissionGroupDefinition组定义,基本同PermissionDefinition,重点方法GetPermissionsWithChildren,是递归获取所有的PermissionDefinition。

    3、PermissionDefinitionContext,是组定义字典的Dictionary<string, PermissionGroupDefinition> Groups,及一些方法AddGroup,GetGroupOrNull方法

    4、IPermissionDefinitionManager,有组的定义,也有PermissionDefinition的方法,构造函数遍历Options.DefinitionProviders,遍历实现中Define方法,填充PermissionDefinitionContext,返回PermissionGroupDefinition的字典

     protected virtual Dictionary<string, PermissionGroupDefinition> CreatePermissionGroupDefinitions()
            {
                var context = new PermissionDefinitionContext();
    
                using (var scope = _serviceProvider.CreateScope())
                {
                    var providers = Options
                        .DefinitionProviders
                        .Select(p => scope.ServiceProvider.GetRequiredService(p) as IPermissionDefinitionProvider)
                        .ToList();
    
                    foreach (var provider in providers)
                    {
                        provider.Define(context);
                    }
                }
    
                return context.Groups;
            }
    

    2 权限的提供者IPermissionValueProvider(三个分别是用户、角色和客户端),其主要方法 Task<PermissionGrantResult> CheckAsync(PermissionValueCheckContext context);返回是结果是Undefined,Granted,Prohibited的其中的一个

     其中:PermissionValueCheckContext的上下文要引入PermissionDefinition要鉴权的权限定义以及ClaimsPrincipal(用户的信息)。PermissionValueCheckContext的构建见PermissionChecker类;它是调用IPermissionValueProvider来获取是否存在权限;var context = new PermissionValueCheckContext(permission, claimsPrincipal);

    IPermissionValueProvider的抽象方法PermissionValueProvider,注入IPermissionStore

     public abstract class PermissionValueProvider : IPermissionValueProvider, ISingletonDependency  //TODO: to transient?
        {
            public abstract string Name { get; }
    
            protected IPermissionStore PermissionStore { get; }
    
            protected PermissionValueProvider(IPermissionStore permissionStore)
            {
                PermissionStore = permissionStore;
            }
    
            public abstract Task<PermissionGrantResult> CheckAsync(PermissionValueCheckContext context);
        }

     其一实现方法:用户UserPermissionValueProvider,用户AbpClaimTypes.UserId,角色AbpClaimTypes.Role,客户端AbpClaimTypes.ClientId,同理。

      public override async Task<PermissionGrantResult> CheckAsync(PermissionValueCheckContext context)
            {
                var userId = context.Principal?.FindFirst(AbpClaimTypes.UserId)?.Value;
    
                if (userId == null)
                {
                    return PermissionGrantResult.Undefined;
                }
    
                return await PermissionStore.IsGrantedAsync(context.Permission.Name, Name, userId)
                    ? PermissionGrantResult.Granted
                    : PermissionGrantResult.Undefined;
            }

    二、IPermissionChecker是检查既定的认证信息是否有权限,见构造函数

    public PermissionChecker(
                IOptions<PermissionOptions> options,
                IServiceProvider serviceProvider,
                ICurrentPrincipalAccessor principalAccessor,
                IPermissionDefinitionManager permissionDefinitionManager, 
                ICurrentTenant currentTenant)
            {
                PrincipalAccessor = principalAccessor;
                PermissionDefinitionManager = permissionDefinitionManager;
                CurrentTenant = currentTenant;
                Options = options.Value;
    
                _lazyProviders = new Lazy<List<IPermissionValueProvider>>(
                    () => Options
                        .ValueProviders
                        .Select(c => serviceProvider.GetRequiredService(c) as IPermissionValueProvider)
                        .ToList(),
                    true
                );
            }
    

      这是权限判断的方法,遍历IPermissionValueProvider在Options里面ValueProviders字典

      public virtual async Task<bool> IsGrantedAsync(ClaimsPrincipal claimsPrincipal, string name)
            {
                Check.NotNull(name, nameof(name));
    
                var permission = PermissionDefinitionManager.Get(name);
    
                var multiTenancySide = claimsPrincipal?.GetMultiTenancySide()
                                       ?? CurrentTenant.GetMultiTenancySide();
    
                if (!permission.MultiTenancySide.HasFlag(multiTenancySide))
                {
                    return false;
                }
    
                var isGranted = false;
                var context = new PermissionValueCheckContext(permission, claimsPrincipal);
                foreach (var provider in ValueProviders)
                {
                    if (context.Permission.Providers.Any() &&
                        !context.Permission.Providers.Contains(provider.Name))
                    {
                        continue;
                    }
    
                    var result = await provider.CheckAsync(context);
    
                    if (result == PermissionGrantResult.Granted)
                    {
                        isGranted = true;
                    }
                    else if (result == PermissionGrantResult.Prohibited)
                    {
                        return false;
                    }
                }
    
                return isGranted;
            }
        
    

     4、IMethodInvocationAuthorizationService,用于拦截器

          protected async Task CheckAsync(IAuthorizeData authorizationAttribute)
            {
                if (authorizationAttribute.Policy == null)
                {
                    //TODO: Can we find a better, unified, way of checking if current request has been authenticated
                    if (!_currentUser.IsAuthenticated && !_currentClient.IsAuthenticated)
                    {
                        throw new AbpAuthorizationException("Authorization failed! User has not logged in.");
                    }
                }
                else
                {
                    await _authorizationService.CheckAsync(authorizationAttribute.Policy);
                }
    
                //TODO: What about roles and other props?
            }

     Asp.net Core的认证和授权,ABP进行集成

    Feature模块

    首先是继承FeatureDefinitionProvider,将Feature组定义,FeatureGroupDefiniftion增加FeatureDefinition,

       public class TestFeatureDefinitionProvider : FeatureDefinitionProvider
        {
            public override void Define(IFeatureDefinitionContext context)
            {
                var group = context.AddGroup("Test Group");
                group.AddFeature("BooleanTestFeature1");
                group.AddFeature("BooleanTestFeature2");
                group.AddFeature("IntegerTestFeature1", defaultValue: "1");
            }
        }

    数据库存储的是FeatureValue,FeatureDefinition的标识Name,ProviderName(Default(D)、Edition、Tenant),ProviderKey,根据需要获取是Value

    IFeatureStore负责获取Value责任 

    IFeatureValueProvider: Name,Task<string> GetOrNullAsync([NotNull] FeatureDefinition feature);获取特性的方式不同,默认直接从定义中获取,版本是从认证获取FindEditionId,租户获取tenantid,作为providerkey,用于给featurestore去得到值

    FeatureChecker是检查方法,它FeatureManage首先根据Name获取得FeatureDefinition,检查允许哪些AllowedProviders,顺序是TenantFeatureValueProvider,EditionFeatureValueProvider,DefaultValueFeatureValueProvider

    再由这些获取值 ,获取得到就返回Value(string),若为空,则是false,否则转移成bool

      public interface IFeatureChecker
        {
            Task<string> GetOrNullAsync([NotNull] string name);
            Task<bool> IsEnabledAsync(string name);
        }

    拦截器上,当类或方法应用标识RequiresFeatureAttribute特性,应用拦截器,将方法的上下文当Context,

    MethodInvocationFeatureCheckerService服务方法是拦截器,当执行方法,首先获取方法上RequiresFeature字符串,再获取类的RequiresFeature字符串,组合在一起

    每个都要检查,默认是至少一个满足,则可通过,也可以设置全部满足才可通过

    /// <summary>
        /// This attribute can be used on a class/method to declare that given class/method is available
        /// only if required feature(s) are enabled.
        /// </summary>
        [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
        public class RequiresFeatureAttribute : Attribute
        {
            /// <summary>
            /// A list of features to be checked if they are enabled.
            /// </summary>
            public string[] Features { get; }
    
            /// <summary>
            /// If this property is set to true, all of the <see cref="Features"/> must be enabled.
            /// If it's false, at least one of the <see cref="Features"/> must be enabled.
            /// Default: false.
            /// </summary>
            public bool RequiresAll { get; set; }
    
            /// <summary>
            /// Creates a new instance of <see cref="RequiresFeatureAttribute"/> class.
            /// </summary>
            /// <param name="features">A list of features to be checked if they are enabled</param>
            public RequiresFeatureAttribute(params string[] features)
            {
                Features = features ?? Array.Empty<string>();
            }
        }

    IFeatureManagementProvider:根据提示FeatureDefinition,providerKey去获取和获取,首先是租户,然后是版本,才是默认

  • 相关阅读:
    点击弹出层以外的区域隐藏弹出层
    css3 animation 动画属性简介
    IdentityServer4 接入自己的用户体系
    分布式事务的实现
    微服务分布式数据管理的挑战
    微服务的数据自治
    SkyWalking 分布式追踪系统
    创建、改进和控制微服务API的版本和契约
    富领域模型和贫血领域模型
    cenos 安装git
  • 原文地址:https://www.cnblogs.com/cloudsu/p/11179967.html
Copyright © 2020-2023  润新知