在整体介绍这个部分之前,如果对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. JavaScript一看就懂(2)闭包

    认识闭包之前需要先了解作用域,如果你对作用域还没有足够了解,请移步JavaScript一看就懂(1)作用域 什么是闭包? 我们可以先简单认为:一个函数a定义在另一个函数b里面,这个函数a就是闭包: f ...

  2. 西安活动 | 2019年1月13号 "拥抱开源, 又见.NET" 线下交流活动报名进行中

    随着.NET Core的发布和开源,.NET又重新回到人们的视野..NET Core的下个3.0即将release,加入非常多的新功能,越来越拥抱变化,DevOps和Microservice的最佳实践 ...

  3. SLAM+语音机器人DIY系列:(二)ROS入门——5.编写简单的消息发布器和订阅器

    摘要 ROS机器人操作系统在机器人应用领域很流行,依托代码开源和模块间协作等特性,给机器人开发者带来了很大的方便.我们的机器人“miiboo”中的大部分程序也采用ROS进行开发,所以本文就重点对ROS ...

  4. Flutter 即学即用系列博客——03 在旧有项目引入 Flutter

    前言 其实如果打算在实际项目中引入 Flutter,完全将旧有项目改造成纯 Flutter 项目的可能性比较小,更多的是在旧有项目引入 Flutter. 因此本篇我们就说一说如何在旧有项目引入 Flu ...

  5. ADO.NET学习(一)

    一.ADO.NET简介 ADO.NET可以看作是C#语言访问数据库的一种方式.编程语言编写的程序需要数据库的支持,那么怎样才能让他们建立连接呢?当然是ADO.NET 二.ADO.NET 整体流程 1) ...

  6. 并发concurrent---1

    背景:并发知识是一个程序员段位升级的体现,同样也是进入BAT的必经之路,有必要把并发知识重新梳理一遍. 并发concurrent: 说到并发concurrent,肯定首先想到了线程,创建线程有两种方法 ...

  7. iis读取不到本地证书问题

    导入证书时,通过mmc命令打开控制台->添加管理单元或删除单元->选择本地计算机账号->然后导入证书,解决 ssl证书无法与www.xxx通信. 证书导入后,不能正常读取.有两个问题 ...

  8. WebGL或OpenGL关于模型视图投影变换的设置技巧

    目录 1. 具体实例 2. 解决方案 1) Cube.html 2) Cube.js 3) 运行结果 3. 详细讲解 1) 模型变换 2) 视图变换 3) 投影变换 4) 模型视图投影矩阵 4. 存在 ...

  9. Intel MKL FATAL ERROR: Cannot load mkl_intel_thread.dll

    Intel MKL FATAL ERROR: Cannot load mkl_intel_thread.dll 在使用Anaconda创建一个虚拟环境出来,然后安装了scikit-learn.nump ...

  10. Java文件上传之SpringMVC实例

    一.配置文件 web.xml <?xml version="1.0" encoding="UTF-8"?><web-app version=& ...