一, 简要说明
在上篇文章里面, 我们在 ApplicationService 当中看到了权限检测代码, 通过注入 IAuthorizationService 就可以实现权限检测. 不过跳转到源码才发现, 这个接口是 ASP.NET Core 原生提供的 "基于策略" 的权限验证接口, 这就说明 ABP vNext 基于原生的授权验证框架进行了自定义扩展.
让我们来看一下 Volo.Abp.Ddd.Application 项目的依赖结构(权限相关).
本篇文章下面的内容基本就会围绕上述框架模块展开, 本篇文章通篇较长, 因为还涉及到 .NET Core Identity 与 IdentityServer4 这两部分. 关于这两部分的内容, 我会在本篇文章大概讲述 ABP vNext 的实现, 关于更加详细的内容, 请查阅官方文档或其他博主的博客.
二, 源码分析
ABP vNext 关于权限验证和权限定义的部分, 都存放在 Volo.Abp.Authorization 和 Volo.Abp.Security 模块内部. 源码分析我都比较喜欢倒推, 即通过实际的使用场景, 反向推导 基础实现, 所以后面文章编写的顺序也将会以这种方式进行.
2.1 Security 基础组件库
这里我们先来到 Volo.Abp.Security, 因为这个模块代码和类型都是最少的. 这个项目都没有模块定义, 说明里面的东西都是定义的一些基础组件.
2.1.1 Claims 与 Identity 的快捷访问
先从第一个扩展方法开始, 这个扩展方法里面比较简单, 它主要是提供对 ClaimsPrincipal 和 IIdentity 的快捷访问方法. 比如我要从 ClaimsPrincipal / IIdentity 获取租户 Id, 用户 Id 等.
- public static class AbpClaimsIdentityExtensions
- {
- public static Guid? FindUserId([NotNull] this ClaimsPrincipal principal)
- {
- Check.NotNull(principal, nameof(principal));
- // 根据 AbpClaimTypes.UserId 查找对应的值.
- var userIdOrNull = principal.Claims?.FirstOrDefault(c => c.Type == AbpClaimTypes.UserId);
- if (userIdOrNull == null || userIdOrNull.Value.IsNullOrWhiteSpace())
- {
- return null;
- }
- // 返回 Guid 对象.
- return Guid.Parse(userIdOrNull.Value);
- }
2.1.2 未授权异常的定义
这个异常我们在老版本 ABP 里面也见到过, 它就是 AbpAuthorizationException . 只要有任何未授权的操作, 都会导致该异常被抛出. 后面我们在讲解 ASP.NET Core MVC 的时候就会知道, 在默认的错误码处理中, 针对于程序抛出的 AbpAuthorizationException , 都会视为 403 或者 401 错误.
- public class DefaultHttpExceptionStatusCodeFinder : IHttpExceptionStatusCodeFinder, ITransientDependency
- {
- // ... 其他代码
- public virtual HttpStatusCode GetStatusCode(HttpContext httpContext, Exception exception)
- {
- // ... 其他代码
- // 根据 HTTP 协议对于状态码的定义, 401 表示的是没有登录的用于尝试访问受保护的资源. 而 403 则表示用户已经登录, 但他没有目标资源的访问权限.
- if (exception is AbpAuthorizationException)
- {
- return httpContext.User.Identity.IsAuthenticated
- ? HttpStatusCode.Forbidden
- : HttpStatusCode.Unauthorized;
- }
- // ... 其他代码
- }
- // ... 其他代码
- }
就 AbpAuthorizationException 异常来说, 它本身并不复杂, 只是一个简单的异常而已. 只是因为它的特殊含义, 在 ABP vNext 处理异常时都会进行特殊处理.
只是在这里我说明一下, ABP vNext 将它所有的异常都设置为可序列化的, 这里的可序列化不仅仅是将 Serialzable 标签打在类上就行了. ABP vNext 还创建了基于 StreamingContext 的构造函数, 方便我们后续对序列化操作进行定制化处理.
- public interface ICurrentUser
- {
- bool IsAuthenticated { get; }
- [CanBeNull]
- Guid? Id { get; }
- [CanBeNull]
- string UserName { get; }
- [CanBeNull]
- string PhoneNumber { get; }
- bool PhoneNumberVerified { get; }
- [CanBeNull]
- string Email { get; }
- bool EmailVerified { get; }
- Guid? TenantId { get; }
- [NotNull]
- string[] Roles { get; }
- [CanBeNull]
- Claim FindClaim(string claimType);
- [NotNull]
- Claim[] FindClaims(string claimType);
- [NotNull]
- Claim[] GetAllClaims();
- bool IsInRole(string roleName);
- }
- public class CurrentUser : ICurrentUser, ITransientDependency
- {
- // ... 其他代码
- public virtual string[] Roles => FindClaims(AbpClaimTypes.Role).Select(c => c.Value).ToArray();
- private readonly ICurrentPrincipalAccessor _principalAccessor;
- public CurrentUser(ICurrentPrincipalAccessor principalAccessor)
- {
- _principalAccessor = principalAccessor;
- }
- // ... 其他代码
- public virtual Claim[] FindClaims(string claimType)
- {
- // 直接使用 LINQ 查询对应的 Type 就能拿到上述信息.
- return _principalAccessor.Principal?.Claims.Where(c => c.Type == claimType).ToArray() ?? EmptyClaimsArray;
- }
- // ... 其他代码
- }
- public interface ICurrentPrincipalAccessor
- {
- ClaimsPrincipal Principal { get; }
- }
- public class ThreadCurrentPrincipalAccessor : ICurrentPrincipalAccessor, ISingletonDependency
- {
- public virtual ClaimsPrincipal Principal => Thread.CurrentPrincipal as ClaimsPrincipal;
- }
- public class HttpContextCurrentPrincipalAccessor : ThreadCurrentPrincipalAccessor
- {
- // 如果没有获取到数据, 则使用 Thread.CurrentPrincipal.
- public override ClaimsPrincipal Principal => _httpContextAccessor.HttpContext?.User ?? base.Principal;
- private readonly IHttpContextAccessor _httpContextAccessor;
- public HttpContextCurrentPrincipalAccessor(IHttpContextAccessor httpContextAccessor)
- {
- _httpContextAccessor = httpContextAccessor;
- }
- }
- // Create a ClaimsPrincipal and set Thread.CurrentPrincipal
- var identity = new ClaimsIdentity();
- identity.AddClaim(new Claim(ClaimTypes.Name, "User1"));
- Thread.CurrentPrincipal = new ClaimsPrincipal(identity);
- // Check the current user
- Console.WriteLine($"Current user: {Thread.CurrentPrincipal?.Identity.Name}");
- // For the method to complete asynchronously
- await Task.Yield();
- // Check the current user after
- Console.WriteLine($"Current user: {Thread.CurrentPrincipal?.Identity.Name}");
- public abstract class PermissionDefinitionProvider : IPermissionDefinitionProvider, ITransientDependency
- {
- public abstract void Define(IPermissionDefinitionContext context);
- }
- public class BloggingPermissionDefinitionProvider : PermissionDefinitionProvider
- {
- public override void Define(IPermissionDefinitionContext context)
- {
- var bloggingGroup = context.AddGroup(BloggingPermissions.GroupName, L("Permission:Blogging"));
- // ... 其他代码.
- var tags = bloggingGroup.AddPermission(BloggingPermissions.Tags.Default, L("Permission:Tags"));
- tags.AddChild(BloggingPermissions.Tags.Update, L("Permission:Edit"));
- tags.AddChild(BloggingPermissions.Tags.Delete, L("Permission:Delete"));
- tags.AddChild(BloggingPermissions.Tags.Create, L("Permission:Create"));
- var comments = bloggingGroup.AddPermission(BloggingPermissions.Comments.Default, L("Permission:Comments"));
- comments.AddChild(BloggingPermissions.Comments.Update, L("Permission:Edit"));
- comments.AddChild(BloggingPermissions.Comments.Delete, L("Permission:Delete"));
- comments.AddChild(BloggingPermissions.Comments.Create, L("Permission:Create"));
- }
- // 使用本地化字符串进行文本显示.
- private static LocalizableString L(string name)
- {
- return LocalizableString.Create<BloggingResource>(name);
- }
- }
- [DependsOn(
- typeof(AbpSecurityModule),
- typeof(AbpLocalizationAbstractionsModule),
- typeof(AbpMultiTenancyModule)
- )]
- public class AbpAuthorizationModule : AbpModule
- {
- public override void PreConfigureServices(ServiceConfigurationContext context)
- {
- // 在 AutoFac 进行组件注册的时候, 根据组件的类型定义视情况绑定拦截器.
- context.Services.OnRegistred(AuthorizationInterceptorRegistrar.RegisterIfNeeded);
- // 在 AutoFac 进行组件注册的时候, 根据组件的类型, 判断是否是 Provider.
- AutoAddDefinitionProviders(context.Services);
- }
- public override void ConfigureServices(ServiceConfigurationContext context)
- {
- // 注册认证授权服务.
- context.Services.AddAuthorization();
- // 替换掉 ASP.NET Core 提供的权限处理器, 转而使用 ABP vNext 提供的权限处理器.
- context.Services.AddSingleton<IAuthorizationHandler, PermissionRequirementHandler>();
- // 这一部分是添加内置的一些权限值检查, 后面我们在将 PermissionChecker 的时候会提到.
- Configure<PermissionOptions>(options =>
- {
- options.ValueProviders.Add<UserPermissionValueProvider>();
- options.ValueProviders.Add<RolePermissionValueProvider>();
- options.ValueProviders.Add<ClientPermissionValueProvider>();
- });
- }
- private static void AutoAddDefinitionProviders(IServiceCollection services)
- {
- var definitionProviders = new List<Type>();
- services.OnRegistred(context =>
- {
- if (typeof(IPermissionDefinitionProvider).IsAssignableFrom(context.ImplementationType))
- {
- definitionProviders.Add(context.ImplementationType);
- }
- });
- // 将获取到的 Provider 传递给 PermissionOptions .
- services.Configure<PermissionOptions>(options =>
- {
- options.DefinitionProviders.AddIfNotContains(definitionProviders);
- });
- }
- }
- protected virtual Dictionary<string, PermissionGroupDefinition> CreatePermissionGroupDefinitions()
- {
- // 创建一个权限定义上下文.
- var context = new PermissionDefinitionContext();
- // 创建一个临时范围用于解析 Provider,Provider 解析完成之后即被释放.
- using (var scope = _serviceProvider.CreateScope())
- {
- // 根据之前的类型, 通过 IoC 进行解析出实例, 指定各个 Provider 的 Define() 方法, 会向权限上下文填充权限.
- var providers = Options
- .DefinitionProviders
- .Select(p => scope.ServiceProvider.GetRequiredService(p) as IPermissionDefinitionProvider)
- .ToList();
- foreach (var provider in providers)
- {
- provider.Define(context);
- }
- }
- // 返回权限组名称 - 权限组定义的字典.
- return context.Groups;
- }
- public class PermissionGroupDefinition
- {
- /// <summary>
- /// 唯一的权限组标识名称.
- /// </summary>
- public string Name { get; }
- // 开发人员针对权限组的一些自定义属性.
- public Dictionary<string, object> Properties { get; }
- // 权限所对应的本地化名称.
- public ILocalizableString DisplayName
- {
- get => _displayName;
- set => _displayName = Check.NotNull(value, nameof(value));
- }
- private ILocalizableString _displayName;
- /// <summary>
- /// 权限的适用范围, 默认是租户 / 租主都适用.
- /// 默认值: <see cref="MultiTenancySides.Both"/>
- /// </summary>
- public MultiTenancySides MultiTenancySide { get; set; }
- // 权限组下面的所属权限.
- public IReadOnlyList<PermissionDefinition> Permissions => _permissions.ToImmutableList();
- private readonly List<PermissionDefinition> _permissions;
- // 针对于自定义属性的快捷索引器.
- public object this[string name]
- {
- get => Properties.GetOrDefault(name);
- set => Properties[name] = value;
- }
- protected internal PermissionGroupDefinition(
- string name,
- ILocalizableString displayName = null,
- MultiTenancySides multiTenancySide = MultiTenancySides.Both)
- {
- Name = name;
- // 没有传递多语言串, 则使用权限组的唯一标识作为显示内容.
- DisplayName = displayName ?? new FixedLocalizableString(Name);
- MultiTenancySide = multiTenancySide;
- Properties = new Dictionary<string, object>();
- _permissions = new List<PermissionDefinition>();
- }
- // 像权限组添加属于它的权限.
- public virtual PermissionDefinition AddPermission(
- string name,
- ILocalizableString displayName = null,
- MultiTenancySides multiTenancySide = MultiTenancySides.Both)
- {
- var permission = new PermissionDefinition(name, displayName, multiTenancySide);
- _permissions.Add(permission);
- return permission;
- }
- // 递归构建权限集合, 因为定义的某个权限内部还拥有子权限.
- public virtual List<PermissionDefinition> GetPermissionsWithChildren()
- {
- var permissions = new List<PermissionDefinition>();
- foreach (var permission in _permissions)
- {
- AddPermissionToListRecursively(permissions, permission);
- }
- return permissions;
- }
- // 递归构建方法.
- private void AddPermissionToListRecursively(List<PermissionDefinition> permissions, PermissionDefinition permission)
- {
- permissions.Add(permission);
- foreach (var child in permission.Children)
- {
- AddPermissionToListRecursively(permissions, child);
- }
- }
- public override string ToString()
- {
- return $"[{nameof(PermissionGroupDefinition)} {Name}]";
- }
- }
- public class PermissionDefinition
- {
- /// <summary>
- /// 唯一的权限标识名称.
- /// </summary>
- public string Name { get; }
- /// <summary>
- /// 当前权限的父级权限, 这个属性的值只可以通过 AddChild() 方法进行设置.
- /// </summary>
- public PermissionDefinition Parent { get; private set; }
- /// <summary>
- /// 权限的适用范围, 默认是租户 / 租主都适用.
- /// 默认值: <see cref="MultiTenancySides.Both"/>
- /// </summary>
- public MultiTenancySides MultiTenancySide { get; set; }
- /// <summary>
- /// 适用的权限值提供者, 这块我们会在后面进行讲解, 为空的时候则使用所有的提供者进行校验.
- /// </summary>
- public List<string> Providers { get; } //TODO: Rename to AllowedProviders?
- // 权限的多语言名称.
- public ILocalizableString DisplayName
- {
- get => _displayName;
- set => _displayName = Check.NotNull(value, nameof(value));
- }
- private ILocalizableString _displayName;
- // 获取权限的子级权限.
- public IReadOnlyList<PermissionDefinition> Children => _children.ToImmutableList();
- private readonly List<PermissionDefinition> _children;
- /// <summary>
- /// 开发人员针对权限的一些自定义属性.
- /// </summary>
- public Dictionary<string, object> Properties { get; }
- // 针对于自定义属性的快捷索引器.
- public object this[string name]
- {
- get => Properties.GetOrDefault(name);
- set => Properties[name] = value;
- }
- protected internal PermissionDefinition(
- [NotNull] string name,
- ILocalizableString displayName = null,
- MultiTenancySides multiTenancySide = MultiTenancySides.Both)
- {
- Name = Check.NotNull(name, nameof(name));
- DisplayName = displayName ?? new FixedLocalizableString(name);
- MultiTenancySide = multiTenancySide;
- Properties = new Dictionary<string, object>();
- Providers = new List<string>();
- _children = new List<PermissionDefinition>();
- }
- public virtual PermissionDefinition AddChild(
- [NotNull] string name,
- ILocalizableString displayName = null,
- MultiTenancySides multiTenancySide = MultiTenancySides.Both)
- {
- var child = new PermissionDefinition(
- name,
- displayName,
- multiTenancySide)
- {
- Parent = this
- };
- _children.Add(child);
- return child;
- }
- /// <summary>
- /// 设置指定的自定义属性.
- /// </summary>
- public virtual PermissionDefinition WithProperty(string key, object value)
- {
- Properties[key] = value;
- return this;
- }
- /// <summary>
- /// 添加一组权限值提供者集合.
- /// </summary>
- public virtual PermissionDefinition WithProviders(params string[] providers)
- {
- if (!providers.IsNullOrEmpty())
- {
- Providers.AddRange(providers);
- }
- return this;
- }
- public override string ToString()
- {
- return $"[{nameof(PermissionDefinition)} {Name}]";
- }
- }
- public interface IPermissionDefinitionManager
- {
- // 根据权限定义的唯一标识获取权限, 一旦不存在就会抛出 AbpException 异常.
- [NotNull]
- PermissionDefinition Get([NotNull] string name);
- // 根据权限定义的唯一标识获取权限, 如果权限不存在, 则返回 null.
- [CanBeNull]
- PermissionDefinition GetOrNull([NotNull] string name);
- // 获取所有的权限.
- IReadOnlyList<PermissionDefinition> GetPermissions();
- // 获取所有的权限组.
- IReadOnlyList<PermissionGroupDefinition> GetGroups();
- }
- protected virtual Dictionary<string, PermissionDefinition> CreatePermissionDefinitions()
- {
- var permissions = new Dictionary<string, PermissionDefinition>();
- // 遍历权限定义组, 这个东西在之前就已经构建好了.
- foreach (var groupDefinition in PermissionGroupDefinitions.Values)
- {
- // 递归子级权限.
- foreach (var permission in groupDefinition.Permissions)
- {
- AddPermissionToDictionaryRecursively(permissions, permission);
- }
- }
- // 返回权限唯一标识 - 权限定义 的字典.
- return permissions;
- }
- protected virtual void AddPermissionToDictionaryRecursively(
- Dictionary<string, PermissionDefinition> permissions,
- PermissionDefinition permission)
- {
- if (permissions.ContainsKey(permission.Name))
- {
- throw new AbpException("Duplicate permission name:" + permission.Name);
- }
- permissions[permission.Name] = permission;
- foreach (var child in permission.Children)
- {
- AddPermissionToDictionaryRecursively(permissions, child);
- }
- }
- public void ConfigureService(IServiceCollection services)
- {
- services.AddAuthorization(options =>
- {
- options.AddPolicy("User", policy => policy
- .RequireAssertion(context => context.User.HasClaim(c => (c.Type == "EmployeeNumber" || c.Type == "Role")))
- );
- // 这里的意思是, 用户角色必须是 Admin, 并且他的用户名是 Alice, 并且必须要有类型为 EmployeeNumber 的 Claim.
- options.AddPolicy("Employee", policy => policy
- .RequireRole("Admin")
- .RequireUserName("Alice")
- .RequireClaim("EmployeeNumber")
- .Combine(commonPolicy));
- });
- }
- public AuthorizationPolicyBuilder RequireClaim(string claimType)
- {
- if (claimType == null)
- {
- throw new ArgumentNullException(nameof(claimType));
- }
- // 构建了一个 ClaimsAuthorizationRequirement 对象, 并添加到策略的 Requirements 组.
- Requirements.Add(new ClaimsAuthorizationRequirement(claimType, allowedValues: null));
- return this;
- }
- 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: 可以使用缓存进行优化.
- // 通过 Builder 构建一个策略.
- var policyBuilder = new AuthorizationPolicyBuilder(Array.Empty<string>());
- // 创建一个 PermissionRequirement 对象添加到限定条件组中.
- policyBuilder.Requirements.Add(new PermissionRequirement(policyName));
- return policyBuilder.Build();
- }
- return null;
- }
- services.AddSingleton<IAuthorizationHandler, BadgeEntryHandler>();
- services.AddSingleton<IAuthorizationHandler, HasTemporaryStickerHandler>();
- public class PermissionRequirement : IAuthorizationRequirement
- {
- public string PermissionName { get; }
- public PermissionRequirement([NotNull]string permissionName)
- {
- Check.NotNull(permissionName, nameof(permissionName));
- PermissionName = permissionName;
- }
- }
- public class PermissionRequirementHandler : AuthorizationHandler<PermissionRequirement>
- {
- // 这里通过权限检查器来确定当前用户是否拥有某个权限.
- private readonly IPermissionChecker _permissionChecker;
- public PermissionRequirementHandler(IPermissionChecker permissionChecker)
- {
- _permissionChecker = permissionChecker;
- }
- protected override async Task HandleRequirementAsync(
- AuthorizationHandlerContext context,
- PermissionRequirement requirement)
- {
- // 如果当前用户拥有某个权限, 则通过 Contxt.Succeed() 通过授权验证.
- if (await _permissionChecker.IsGrantedAsync(context.User, requirement.PermissionName))
- {
- context.Succeed(requirement);
- }
- }
- }
- public interface IPermissionChecker
- {
- Task<bool> IsGrantedAsync([NotNull]string name);
- Task<bool> IsGrantedAsync([CanBeNull] ClaimsPrincipal claimsPrincipal, [NotNull]string name);
- }
- 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;
- // 这里是重点哦, 这个权限值检测上下文是之前没有说过的东西, 说白了就是针对不同维度的权限检测.
- // 之前这部分东西是通过权限策略下面的 Requirement 提供的, 这里 ABP vNext 将其抽象为 PermissionValueProvider.
- var context = new PermissionValueCheckContext(permission, claimsPrincipal);
- foreach (var provider in PermissionValueProviderManager.ValueProviders)
- {
- // 如果指定的权限允许的权限值提供者集合不包含当前的 Provider, 则跳过处理.
- if (context.Permission.Providers.Any() &&
- !context.Permission.Providers.Contains(provider.Name))
- {
- continue;
- }
- // 调用 Provider 的检测方法, 传入身份证明和权限定义进行具体校验.
- var result = await provider.CheckAsync(context);
- // 根据返回的结果, 判断是否通过了权限校验.
- if (result == PermissionGrantResult.Granted)
- {
- isGranted = true;
- }
- else if (result == PermissionGrantResult.Prohibited)
- {
- return false;
- }
- }
- // 返回 true 说明已经授权, 返回 false 说明是没有授权的.
- return isGranted;
- }
- 2.2.6 PermissionValueProvider
- public class UserPermissionValueProvider : PermissionValueProvider
- {
- // 提供者的名称.
- public const string ProviderName = "User";
- public override string Name => ProviderName;
- public UserPermissionValueProvider(IPermissionStore permissionStore)
- : base(permissionStore)
- {
- }
- public override async Task<PermissionGrantResult> CheckAsync(PermissionValueCheckContext context)
- {
- // 从传入的 Principal 中查找 UserId, 不存在则说明没有定义, 视为未授权.
- var userId = context.Principal?.FindFirst(AbpClaimTypes.UserId)?.Value;
- if (userId == null)
- {
- return PermissionGrantResult.Undefined;
- }
- // 调用 IPermissionStore 从持久化存储中, 检测指定权限在某个提供者下面是否已经被授予了权限.
- // 如果被授予了权限, 则返回 true, 没有则返回 false.
- return await PermissionStore.IsGrantedAsync(context.Permission.Name, Name, userId)
- ? PermissionGrantResult.Granted
- : PermissionGrantResult.Undefined;
- }
- }
- private static bool AnyMethodHasAuthorizeAttribute(Type implementationType)
- {
- return implementationType
- .GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
- .Any(HasAuthorizeAttribute);
- }
- private static bool HasAuthorizeAttribute(MemberInfo methodInfo)
- {
- return methodInfo.IsDefined(typeof(AuthorizeAttribute), true);
- }
- public override async Task InterceptAsync(IAbpMethodInvocation invocation)
- {
- // 防止重复检测.
- if (AbpCrossCuttingConcerns.IsApplied(invocation.TargetObject, AbpCrossCuttingConcerns.Authorization))
- {
- await invocation.ProceedAsync();
- return;
- }
- // 将被调用的方法传入, 验证是否允许访问.
- await AuthorizeAsync(invocation);
- await invocation.ProceedAsync();
- }
- protected virtual async Task AuthorizeAsync(IAbpMethodInvocation invocation)
- {
- await _methodInvocationAuthorizationService.CheckAsync(
- new MethodInvocationAuthorizationContext(
- invocation.Method
- )
- );
- }
- protected async Task CheckAsync(IAuthorizeData authorizationAttribute)
- {
- if (authorizationAttribute.Policy == null)
- {
- // 如果当前调用者没有进行认证, 则抛出未登录的异常.
- if (!_currentUser.IsAuthenticated && !_currentClient.IsAuthenticated)
- {
- throw new AbpAuthorizationException("Authorization failed! User has not logged in.");
- }
- }
- else
- {
- // 通过 IAuthorizationService 校验当前用户是否拥有 authorizationAttribute.Policy 权限.
- await _authorizationService.CheckAsync(authorizationAttribute.Policy);
- }
- }
来源: https://www.cnblogs.com/myzony/p/11312928.html