权限模块和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
的快捷方法。
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去获取和获取,首先是租户,然后是版本,才是默认