在整体介绍这个部分之前,如果对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的更多相关文章

  1. ABP拦截器之UnitOfWorkRegistrar(二)

    在上面一篇中我们主要是了解了在ABP系统中是如何使用UnitOfWork以及整个ABP系统中如何执行这些过程的,那么这一篇就让我们来看看UnitOfWorkManager中在执行Begin和Compl ...

  2. ABP拦截器之UnitOfWorkRegistrar(一)

    ABP中UnitOfWorkRegistrar拦截器是整个ABP中非常关键的一个部分,这个部分在整个业务系统中也是用的最多的一个部分,这篇文章的主要思路并不是写如何使用ABP中的UnitOfWork, ...

  3. ABP中的拦截器之ValidationInterceptor(上)

    从今天这一节起就要深入到ABP中的每一个重要的知识点来一步步进行分析,在进行介绍ABP中的拦截器之前我们先要有个概念,到底什么是拦截器,在介绍这些之前,我们必须要了解AOP编程思想,这个一般翻译是面向 ...

  4. (实用篇)浅谈PHP拦截器之__set()与__get()的理解与使用方法

    "一般来说,总是把类的属性定义为private,这更符合现实的逻辑. 但是,对属性的读取和赋值操作是非常频繁的,因此在PHP5中,预定义了两个函数"__get()"和&q ...

  5. Java过滤器与SpringMVC拦截器之间的关系与区别

    今天学习和认识了一下,过滤器和SpringMVC的拦截器的区别,学到了不少的东西,以前一直以为拦截器就是过滤器实现的,现在想想还真是一种错误啊,而且看的比较粗浅,没有一个全局而又细致的认识,由于已至深 ...

  6. PHP拦截器之__set()与__get()的理解与使用

    “一般来说,总是把类的属性定义为private,这更符合现实的逻辑.但是,对属性的读取和赋值操作是非常频繁的,因此在PHP5中,预定义了两个函数“__get()”和“__set()”来获取和赋值其属性 ...

  7. PHP拦截器之__set()与__get()的理解与使用方法

    “一般来说,总是把类的属性定义为private,这更符合现实的逻辑.   但是,对属性的读取和赋值操作是非常频繁的,因此在PHP5中,预定义了两个函数“__get()”和“__set()”来获取和赋值 ...

  8. ABP中的拦截器之AuditingInterceptor

    在上面两篇介绍了ABP中的ValidationInterceptor之后,我们今天来看看ABP中定义的另外一种Interceptor即为AuditingInterceptor,顾名思义就是一种审计相关 ...

  9. ABP中的拦截器之EntityHistoryInterceptor

    今天我们接着之前的系列接着来写另外一种拦截器EntityHistoryInterceptor,这个拦截器到底是做什么的呢?这个从字面上理解是实体历史?这个到底是什么意思?带着这个问题我们来一步步去分析 ...

随机推荐

  1. asp.net后台管理系统-登陆模块-是否自动登陆

    FormsAuthentication.SetAuthCookie(UserFlag, createPersistentCookie); createPersistentCookie是否永久保存coo ...

  2. Eclipse中安装git后pull远程仓库出现错误解决方法

    该图中位置为false 在配置文件中添加如下语句 -Dhttps.protocols=TLSv1,TLSv1.1,TLSv1.2

  3. nginx系列13:最少连接算法以及如何跨worker进程生效

    最少连接算法 使用最少连接算法可以使得nginx优先选择连接最少的上游服务器,需要用到upstream_least_conn模块. 如何跨worker进程生效 因为nginx是多进程结构的,默认多个w ...

  4. ServiceStack.Redis 请求次数6000次异常

    Redis是一个非常NB的内存级的数据库,我们可以把很多”热数据“(即读写非常多的数据)放入其中来操作,这样就减少了和关系型数据库(如SqlServer/My Sql等)之间的交互,程序的响应速度也大 ...

  5. [笔记]记录原开发工作在base命名空间下扩展的属性与方法

    前言 该笔记只是为了记录以前开发使用的方式. 处理命名空间namespace /** * 处理命名空间 * @param {string} 空间名称,可多个 * @return {object} 对象 ...

  6. C#中try catch finally 用法

    1.将预见可能引发异常的代码包含在try语句块中. 2.如果发生了异常,则转入catch的执行. catch有几种写法: catch  这将捕获任何发生的异常. catch(Exception e)  ...

  7. 巧用Handler获取View控件信息

    众所周知,在Android实际开发中,对于某些复杂多变的情况,控件的位置摆放.大小控制并非是xml类型的layout文件完全可以搞定的.此时,我们通常会使用Java代码来通过动态计算,将指定的控件摆放 ...

  8. 怎樣添加設置GridView,CheckBox全選功能

    GridView內CheckBox控件全選設置 不需要添加後台代碼操作,前端即可完成設置,如下: 前端代碼: 1.設置javascript. <html xmlns="http://w ...

  9. C# Dictionary 函数解析及使用方法

    要使用Dictionary集合,需要导入C#泛型命名空间 System.Collections.Generic(程序集:mscorlib)  Dictionary的描述 1.从一组键(Key)到一组值 ...

  10. Maven settings.xml

    <?xml version="1.0" encoding="UTF-8"?> <settings xmlns="http://mav ...