在整体介绍这个部分之前,如果对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. 查询拼接SQL语句,多条件模糊查询

    多条件查询,使用StringBuilder拼接SQL语句,效果如下: 当点击按钮时代码如下: private void button1_Click(object sender, EventArgs e ...

  2. Java 设置PDF文档背景色

    一般生成的PDF文档默认的文档底色为白色,我们可以通过一定方法来更改文档的背景色,以达到文档美化以及保护双眼的作用. 以下内容提供了Java编程来设置PDF背景色的方法.包括: 设置纯色背景色 设置图 ...

  3. Java开发笔记(八十一)如何使用系统自带的注解

    之前介绍继承的时候,提到对于子类而言,父类的普通方法可以重写也可以不重写,但是父类的抽象方法是必须重写的,如果不重写,编译器就直接在子类名称那里显示红叉报错.例如,以前演示抽象类用法之时,曾经把Chi ...

  4. Error fetching https://gems.ruby-china.org/: bad response Not Found 404 (https://gems.ruby-china.org/specs.4.8.gz) 报错解决办法

    执行换源操作 gem source -a https://gems.ruby-china.org/ 时报错: Error fetching https://gems.ruby-china.org/: ...

  5. 命令模式 Command 行为型 设计模式(十八)

    命令模式(Command) 请分析上图中这条命令的涉及到的角色以及执行过程,一种可能的理解方式是这样子的: 涉及角色为:大狗子和大狗子他妈 过程为:大狗子他妈角色 调用 大狗子的“回家吃饭”方法 引子 ...

  6. 解决html代码文本复制问题,js技术

    实例代码: <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <scr ...

  7. 网页三剑客:HTML+CSS+JavaScript 之JavaScript

    JavaScript 简介 JavaScript 是互联网上最流行的脚本语言,这门语言可用于 HTML 和 web,更可广泛用于服务器.PC.笔记本电脑.平板电脑和智能手机等设备. JavaScrip ...

  8. API测试工具SoapUI & Postman对比分析

    本文由葡萄城技术团队于博客园原创并首发 转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具.解决方案和服务,赋能开发者. 最近公司要引入API测试工具,经过调查和了解,最终决定在SoapUI ...

  9. Flutter项目之app升级方案

    题接上篇的文章的项目,还是那个空货管理app.本篇文章用于讲解基于Flutter的app项目的升级方案. 在我接触Flutter之前,做过一个比较失败的基于DCloud的HTML5+技术的app,做过 ...

  10. 【English】三、以o结尾单词变复数

    一.以O结尾的词,许多加es构成复数,特别是一些常用词如: potatoes      土豆 tomatoes     西红柿 echoes 回声 tornadoes    龙卷风 torpedoes ...