ABP拦截器之AuthorizationInterceptor
在整体介绍这个部分之前,如果对ABP中的权限控制还没有一个很明确的认知,请先阅读这篇文章,然后在读下面的内容。
AuthorizationInterceptor看这个名字我们就知道这个拦截器拦截用户一些常规验证操作的,包括用户的登陆信息以及一些Features和Permissions的操作,那么这个过程到底是怎样的呢?这个还是和之前以前,首先在ABP的AbpBootstrapper的AddInterceptorRegistrars方法中添加拦截器的注册类。
private void AddInterceptorRegistrars()
{
ValidationInterceptorRegistrar.Initialize(IocManager);
AuditingInterceptorRegistrar.Initialize(IocManager);
EntityHistoryInterceptorRegistrar.Initialize(IocManager);
UnitOfWorkRegistrar.Initialize(IocManager);
AuthorizationInterceptorRegistrar.Initialize(IocManager);
}
然后跟着代码,我们来一步步看看AuthorizationInterceptorRegistrar这个拦截器注册类中到底做了些什么?
/// <summary>
/// This class is used to register interceptors on the Application Layer.
/// </summary>
internal static class AuthorizationInterceptorRegistrar
{
public static void Initialize(IIocManager iocManager)
{
iocManager.IocContainer.Kernel.ComponentRegistered += Kernel_ComponentRegistered;
} private static void Kernel_ComponentRegistered(string key, IHandler handler)
{
if (ShouldIntercept(handler.ComponentModel.Implementation))
{
handler.ComponentModel.Interceptors.Add(new InterceptorReference(typeof(AuthorizationInterceptor)));
}
} private static bool ShouldIntercept(Type type)
{
if (SelfOrMethodsDefinesAttribute<AbpAuthorizeAttribute>(type))
{
return true;
} if (SelfOrMethodsDefinesAttribute<RequiresFeatureAttribute>(type))
{
return true;
} return false;
} private static bool SelfOrMethodsDefinesAttribute<TAttr>(Type type)
{
if (type.GetTypeInfo().IsDefined(typeof(TAttr), true))
{
return true;
} return type
.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
.Any(m => m.IsDefined(typeof(TAttr), true));
}
}
这个类的作用顾名思义:在应用层注册拦截器。这个类的Initialize方法中还是订阅整个ABP框架中唯一的依赖注入容器IocContainer的ComponentRegistered这个方法,在这个方法的订阅函数中还是用来限定到你哪些类型可以使用当前的AuthorizationInterceptor,通过查看ShouldIntercept方法,我们发现如果当前类型或者当前类型所属的任何一个方法定义了自定义属性AbpAuthorizeAttribute或者是RequiresFeatureAttribute的时候那么就能够使用AuthorizationInterceptor这个拦截器,知道了使用这个拦截器,那么这个拦截器到底能够做些什么呢?
我们首先来看一看这个AuthorizationInterceptor这个方法到底做了些什么?
public class AuthorizationInterceptor : IInterceptor
{
private readonly IAuthorizationHelper _authorizationHelper; public AuthorizationInterceptor(IAuthorizationHelper authorizationHelper)
{
_authorizationHelper = authorizationHelper;
} public void Intercept(IInvocation invocation)
{
_authorizationHelper.Authorize(invocation.MethodInvocationTarget, invocation.TargetType);
invocation.Proceed();
}
}
在这个类中,通过构造函数来注入了IAuthorizationHelper 这个接口,然后再Intercept方法中调用这个接口的Authorize方法,我们知道在我们执行客户端请求的方法之前依赖注入容器会拦截当前的方法,并执行Intercept这个方法,在执行当前方法之前强制插入一些操作,比如现在的这个_authorizationHelper.Authorize这个方法,在该方法执行之后再执行客户端请求的方法,这个是AOP思想的具体体现。
我们现在来看IAuthorizationHelper 接口的具体实现类中Authorize方法到底做了些什么?
public class AuthorizationHelper : IAuthorizationHelper, ITransientDependency
{
public IAbpSession AbpSession { get; set; }
public IPermissionChecker PermissionChecker { get; set; }
public IFeatureChecker FeatureChecker { get; set; }
public ILocalizationManager LocalizationManager { get; set; } private readonly IFeatureChecker _featureChecker;
private readonly IAuthorizationConfiguration _authConfiguration; public AuthorizationHelper(IFeatureChecker featureChecker, IAuthorizationConfiguration authConfiguration)
{
_featureChecker = featureChecker;
_authConfiguration = authConfiguration;
AbpSession = NullAbpSession.Instance;
PermissionChecker = NullPermissionChecker.Instance;
LocalizationManager = NullLocalizationManager.Instance;
} public virtual async Task AuthorizeAsync(IEnumerable<IAbpAuthorizeAttribute> authorizeAttributes)
{
if (!_authConfiguration.IsEnabled)
{
return;
} if (!AbpSession.UserId.HasValue)
{
throw new AbpAuthorizationException(
LocalizationManager.GetString(AbpConsts.LocalizationSourceName, "CurrentUserDidNotLoginToTheApplication")
);
} foreach (var authorizeAttribute in authorizeAttributes)
{
await PermissionChecker.AuthorizeAsync(authorizeAttribute.RequireAllPermissions, authorizeAttribute.Permissions);
}
} public virtual async Task AuthorizeAsync(MethodInfo methodInfo, Type type)
{
await CheckFeatures(methodInfo, type);
await CheckPermissions(methodInfo, type);
} protected virtual async Task CheckFeatures(MethodInfo methodInfo, Type type)
{
var featureAttributes = ReflectionHelper.GetAttributesOfMemberAndType<RequiresFeatureAttribute>(methodInfo, type); if (featureAttributes.Count <= 0)
{
return;
} foreach (var featureAttribute in featureAttributes)
{
await _featureChecker.CheckEnabledAsync(featureAttribute.RequiresAll, featureAttribute.Features);
}
} protected virtual async Task CheckPermissions(MethodInfo methodInfo, Type type)
{
if (!_authConfiguration.IsEnabled)
{
return;
} if (AllowAnonymous(methodInfo, type))
{
return;
} var authorizeAttributes =
ReflectionHelper
.GetAttributesOfMemberAndType(methodInfo, type)
.OfType<IAbpAuthorizeAttribute>()
.ToArray(); if (!authorizeAttributes.Any())
{
return;
} await AuthorizeAsync(authorizeAttributes);
} private static bool AllowAnonymous(MemberInfo memberInfo, Type type)
{
return ReflectionHelper
.GetAttributesOfMemberAndType(memberInfo, type)
.OfType<IAbpAllowAnonymousAttribute>()
.Any();
}
}
在这个类中,首先调用的就是下面的方法
public virtual async Task AuthorizeAsync(MethodInfo methodInfo, Type type)
{
await CheckFeatures(methodInfo, type);
await CheckPermissions(methodInfo, type);
}
那么我们来首先看看CheckFeatures这个方法到底做了些什么?
protected virtual async Task CheckFeatures(MethodInfo methodInfo, Type type)
{
var featureAttributes = ReflectionHelper.GetAttributesOfMemberAndType<RequiresFeatureAttribute>(methodInfo, type); if (featureAttributes.Count <= 0)
{
return;
} foreach (var featureAttribute in featureAttributes)
{
await _featureChecker.CheckEnabledAsync(featureAttribute.RequiresAll, featureAttribute.Features);
}
}
在这个方法内部首先通过反射来获取当前方法是否定义过RequiresFeatureAttribute这个自定义的属性,如果定义过那么获取这些自定义的RequireFeature,如果没有定义过那么就直接返回,定义过就通过一个循环来遍历所有的RequiresFeature,然后调用CheckEnableAsync这个方法,我们一步步来看这个方法内部到底做了些什么?
/// <summary>
/// Checks if one or all of the given features are enabled. Throws <see cref="AbpAuthorizationException"/> if not.
/// </summary>
/// <param name="featureChecker"><see cref="IFeatureChecker"/> instance</param>
/// <param name="requiresAll">True, to require that all the given features are enabled. False, to require one or more.</param>
/// <param name="featureNames">Names of the features</param>
public static async Task CheckEnabledAsync(this IFeatureChecker featureChecker, bool requiresAll, params string[] featureNames)
{
if (featureNames.IsNullOrEmpty())
{
return;
} if (requiresAll)
{
foreach (var featureName in featureNames)
{
if (!(await featureChecker.IsEnabledAsync(featureName)))
{
throw new AbpAuthorizationException(
"Required features are not enabled. All of these features must be enabled: " +
string.Join(", ", featureNames)
);
}
}
}
else
{
foreach (var featureName in featureNames)
{
if (await featureChecker.IsEnabledAsync(featureName))
{
return;
}
} throw new AbpAuthorizationException(
"Required features are not enabled. At least one of these features must be enabled: " +
string.Join(", ", featureNames)
);
}
}
这个方法是定义在FeatureCheckerExtensions这个扩展类中的,按照该方法的注释,这个方法主要用来检查当前Feature是否是enabled,在这个方法的内部,如果当前的RequireFeature的RequiresAll属性为true那么就会检查当前RequireFeature的Features的每一个值是否都是true,如果有一个不为true,那么就会提示:Required features are not enabled. All of these features must be enabled:的错误提示信息。如果当前的RequireFeature的RequiresAll属性为false,那么就就会检查当前RequireFeature的Features的任何一个值是否为true,只要有一个满足条件就返回,如果所有的Features对应的值都为false,那么就会抛出异常信息:Required features are not enabled. At least one of these features must be enabled:这个就是CheckFeatures的全部过程,通过这个过程我们知道主要是检查当前定义了RequireFeature的自定义属性的方法是否满足其内部的Features值是否为true的过程。
在说完CheckFeatures之后我们再来看看CheckPermissions方法,看看这个方法的内部到底做了些什么?
protected virtual async Task CheckPermissions(MethodInfo methodInfo, Type type)
{
if (!_authConfiguration.IsEnabled)
{
return;
} if (AllowAnonymous(methodInfo, type))
{
return;
} var authorizeAttributes =
ReflectionHelper
.GetAttributesOfMemberAndType(methodInfo, type)
.OfType<IAbpAuthorizeAttribute>()
.ToArray(); if (!authorizeAttributes.Any())
{
return;
} await AuthorizeAsync(authorizeAttributes);
}
在这个方法内部,首先看AuthorizationConfiguration是否已经配置为false,如果定义为false,那么后面的过程就不会再继续,通常这个AuthorizationConfiguration内部的IsEnable的配置通常都在当前的Module的PreInitialize方法中可以进行相关配置,如果当前属性为true那么,就接下来验证后面的过程,首先通过调用AllowAnonymous(methodInfo, type)方法来验证当前方法是否允许匿名进行登录,具体的原理是,验证当前方法是否有继承自IAbpAllowAnonymousAttribute这个接口的自定义属性(CustomAttribute),如果继承自定义过这个接口那么就跳过当前检验过程,最后再检验当前的方法所属的类型是否定义过继承自IAbpAuthorizeAttribute接口的自定义属性,如果没有这些自定义属性,那么也会跳过CheckPermissions方法,如果都不满足上述条件,最后就会去校验当前方法继承自IAbpAuthorizeAttribute接口的自定义属性,具体校验的过程在子方法AuthorizeAsync(authorizeAttributes)中进行,我们来看看这个子方法到底做了些什么?
public virtual async Task AuthorizeAsync(IEnumerable<IAbpAuthorizeAttribute> authorizeAttributes)
{
if (!_authConfiguration.IsEnabled)
{
return;
} if (!AbpSession.UserId.HasValue)
{
throw new AbpAuthorizationException(
LocalizationManager.GetString(AbpConsts.LocalizationSourceName, "CurrentUserDidNotLoginToTheApplication")
);
} foreach (var authorizeAttribute in authorizeAttributes)
{
await PermissionChecker.AuthorizeAsync(authorizeAttribute.RequireAllPermissions, authorizeAttribute.Permissions);
}
}
在这个方法的内部,首先也是判断AuthorizationConfiguration是否已经配置为false,如果配置为false,那么就跳过下面的过程,接下来会检验AbpSession.UserId.HasValue是否有值,这个AbpSession.UserId就是当前登录用户的Id,如果当前UserId为Null,那么当客户端请求这个方法的时候,服务端直接抛出异常,并提示:CurrentUserDidNotLoginToTheApplication,如果当前用户已经登录过,最后再验证当前方法的每一个继承自IAbpAuthorizeAttribute接口的自定义属性,具体的验证过程是在PermissionChecker中定义的,我们到PermissionChecker中看看这个过程到底是怎么样的?
/// <summary>
/// Authorizes current user for given permission or permissions,
/// throws <see cref="AbpAuthorizationException"/> if not authorized.
/// </summary>
/// <param name="permissionChecker">Permission checker</param>
/// <param name="requireAll">
/// If this is set to true, all of the <see cref="permissionNames"/> must be granted.
/// If it's false, at least one of the <see cref="permissionNames"/> must be granted.
/// </param>
/// <param name="permissionNames">Name of the permissions to authorize</param>
/// <exception cref="AbpAuthorizationException">Throws authorization exception if</exception>
public static async Task AuthorizeAsync(this IPermissionChecker permissionChecker, bool requireAll, params string[] permissionNames)
{
if (await IsGrantedAsync(permissionChecker, requireAll, permissionNames))
{
return;
} var localizedPermissionNames = LocalizePermissionNames(permissionChecker, permissionNames); if (requireAll)
{
throw new AbpAuthorizationException(
string.Format(
L(
permissionChecker,
"AllOfThesePermissionsMustBeGranted",
"Required permissions are not granted. All of these permissions must be granted: {0}"
),
string.Join(", ", localizedPermissionNames)
)
);
}
else
{
throw new AbpAuthorizationException(
string.Format(
L(
permissionChecker,
"AtLeastOneOfThesePermissionsMustBeGranted",
"Required permissions are not granted. At least one of these permissions must be granted: {0}"
),
string.Join(", ", localizedPermissionNames)
)
);
}
}
我们在解释这个方法之前首先来看看这个方法的注释:Authorizes current user for given permission or permissions,按照解释,这个方法是验证当前登录用户User是否有特定的Permissions,这个方法也是传递两个参数,一个是 requireAll 另外一个是 string[] permissionNames,首先如果定义的requireAll 为true,那么就必须保证当前当前用户必须授予了所有的permissionNames中定义的内容,如果为false,那么只需要满足其中定义的任何一个Permission即可,如果不满足这些定义的Permission,那么就直接抛出异常,那么当前方法就会由于当前登录用户没有被授予相应的Permission而不能执行当前方法,这在一定的程度上保证了同一个方法不同的用户登录时会拥有不同的结果,当然这个很多人可能对之前提到的ABP中的Feature和Permission还不太了解,那么在接下来我会对这些内容来进行单独的介绍,欢迎关注后续文章。
这篇文章就介绍到这里,点击这里返回整个ABP系列的主目录。
ABP拦截器之AuthorizationInterceptor的更多相关文章
- ABP拦截器之UnitOfWorkRegistrar(二)
在上面一篇中我们主要是了解了在ABP系统中是如何使用UnitOfWork以及整个ABP系统中如何执行这些过程的,那么这一篇就让我们来看看UnitOfWorkManager中在执行Begin和Compl ...
- ABP拦截器之UnitOfWorkRegistrar(一)
ABP中UnitOfWorkRegistrar拦截器是整个ABP中非常关键的一个部分,这个部分在整个业务系统中也是用的最多的一个部分,这篇文章的主要思路并不是写如何使用ABP中的UnitOfWork, ...
- ABP中的拦截器之ValidationInterceptor(上)
从今天这一节起就要深入到ABP中的每一个重要的知识点来一步步进行分析,在进行介绍ABP中的拦截器之前我们先要有个概念,到底什么是拦截器,在介绍这些之前,我们必须要了解AOP编程思想,这个一般翻译是面向 ...
- (实用篇)浅谈PHP拦截器之__set()与__get()的理解与使用方法
"一般来说,总是把类的属性定义为private,这更符合现实的逻辑. 但是,对属性的读取和赋值操作是非常频繁的,因此在PHP5中,预定义了两个函数"__get()"和&q ...
- Java过滤器与SpringMVC拦截器之间的关系与区别
今天学习和认识了一下,过滤器和SpringMVC的拦截器的区别,学到了不少的东西,以前一直以为拦截器就是过滤器实现的,现在想想还真是一种错误啊,而且看的比较粗浅,没有一个全局而又细致的认识,由于已至深 ...
- PHP拦截器之__set()与__get()的理解与使用
“一般来说,总是把类的属性定义为private,这更符合现实的逻辑.但是,对属性的读取和赋值操作是非常频繁的,因此在PHP5中,预定义了两个函数“__get()”和“__set()”来获取和赋值其属性 ...
- PHP拦截器之__set()与__get()的理解与使用方法
“一般来说,总是把类的属性定义为private,这更符合现实的逻辑. 但是,对属性的读取和赋值操作是非常频繁的,因此在PHP5中,预定义了两个函数“__get()”和“__set()”来获取和赋值 ...
- ABP中的拦截器之AuditingInterceptor
在上面两篇介绍了ABP中的ValidationInterceptor之后,我们今天来看看ABP中定义的另外一种Interceptor即为AuditingInterceptor,顾名思义就是一种审计相关 ...
- ABP中的拦截器之EntityHistoryInterceptor
今天我们接着之前的系列接着来写另外一种拦截器EntityHistoryInterceptor,这个拦截器到底是做什么的呢?这个从字面上理解是实体历史?这个到底是什么意思?带着这个问题我们来一步步去分析 ...
随机推荐
- 强化学习(十一) Prioritized Replay DQN
在强化学习(十)Double DQN (DDQN)中,我们讲到了DDQN使用两个Q网络,用当前Q网络计算最大Q值对应的动作,用目标Q网络计算这个最大动作对应的目标Q值,进而消除贪婪法带来的偏差.今天我 ...
- 让ASP.NET Core支持GraphQL之-GraphQL的实现原理
众所周知RESTful API是目前最流行的软件架构风格之一,它主要用于客户端和服务器交互类的软件.基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制. RESTful的优越性是毋庸置疑 ...
- Nginx反向代理和Node.js后端解决跨域问题
最近在写自己的博客,涉及到跨域的问题,自己捣鼓许久,终于解决了.然后总结一下,记录一下,日后遇到类似的问题的时候也可以得到一些启发. 一.什么是跨域 跨域,指的是浏览器不能执行其他网站的脚本.它是由浏 ...
- Vue + WebApi 小项目:构造自己的在线 Markdown 笔记本应用
Vue + WebApi 小项目:构造自己的在线 Markdown 笔记本应用 目录 概要 知识点 完整示例图 代码与资源文件 流程步骤 概要 基于 MVP 最小可行性产品设计理念,我们先完成一个可以 ...
- Java_基础篇(数组排序)
Java_基础之数组排序(从小到大) 1.冒泡排序: 冒泡排序可以写成两层循环. 每次循环将最大的数值交换到数组的最后一个. 每排序完一次,后面就少比较一次.所以二层循环的判断条件写成:arry.le ...
- informix存储过程笔记
一.存储过程概述 存储过程是一个用户定义的函数,由存储过程语句(SPL) 和一组SQL语句组成,以可以执行代码形式存储在数据库中,和表.视图.索引等一样,是数据库的一种对象. 存储过程语言SPL(St ...
- 实现DataTables搜索框查询结果高亮显示
DataTables是封装好的HTML表格插件,丰富了HTML表格的样式,提供了即时搜索.分页等多种表格高级功能.用户可以编写很少的代码(甚至只是使用官方的示例代码),做出一个漂亮的表格以展示数据.关 ...
- Oracle字符到数值转换错误
[错误] [问题分析] line 3: 定义 NUM_VAL varchar2(500); line 9: NUM_VAL := 'NUM'+1; NUM_VAL是一个varchar类型的数据,而在数 ...
- js实现语音功能
在项目中需要对ajax请求返回的消息进行语音播报.那么什么录制的就是在太low啦.下面js贴代码 str 为返回的data //语音播报function voiceAnnouncements(str) ...
- SSM —— 注解解析
@Component是所有受Spring 管理组件的通用形式,@Component注解可以放在类的头上,@Component不推荐使用. @Controller通过@Controller注解说明该类非 ...