spring security多个权限_Spring Security 的权限验证

本文深入探讨了Spring Security的权限验证机制,重点关注FilterSecurityInterceptor和AbstractSecurityInterceptor的作用。FilterSecurityInterceptor作为Servlet Filter的适配器,负责在FilterChain中进行权限验证。AbstractSecurityInterceptor则实现真正的权限验证逻辑,包含beforeInfocation、afterInfocation和finallyInfocation方法。此外,文章还提及了AccessDecisionManager和AfterInvocationManager在权限验证过程中的角色,以及ConfigAttribute用于存储安全配置。
摘要由CSDN通过智能技术生成

在前面的文章里,我们对 Spring Security 进行权限验证的组件有了大致的了解,我们首先来回顾并探究一下细节。

73546a53018479ee7eb3f8a5f7da874f.png

本文涉及到的组件

FilterSecurityInterceptor

这是 AbstractSecurityInterceptor 的一个子类,并且实现了 Filter接口,负责调用父类的 beforeInvocation()afterInvocatio()finallyInvocation() 方法以及一些 Servlet 相关的工作。真正处理权限验证的代码,其实在父类中。它存在的意义就是为了能在 Filter中进行权限验证。

这个 Filter 默认总是被安排在 SecurityFilterChain的最后,因为需要保证它在所有的身份认证相关的 Filter 之后。

AbstractSecurityInterceptor

这个类实现了真正的权限验证的逻辑,它有多个子类,是为了适配不同的技术而存在的,比如上面的FilterSecurityInterceptor 就是为了适配 Servlet Filter 而存在的。

我们可以关注一下上面提到的三个方法,这是每个子类都会调用的。

子类的实现总是下面的套路:

InterceptorStatusToken token = super.beforeInvocation(secureObject); // 1
try {
  // call target method, eg, filterChain.doFilter()
  // may get a returnedObject
} final {
  super.finallyInvocation(token);
}
super.afterInvocation(token, returnedObject);
  1. secureObject 是一个方法调用,它的类型是 Object,但一般会看到MethodInvocation 或者 FilterInvocation 这样的类型。
beforeInfocation 方法

这个方法的目标是调用 AccessDecisionManager.decide() 方法,完成 pre-invocation handling 操作。

在前面的概览中介绍过,AccessDecisionManager.decide()方法有三个参数。其中的 secureObject 已经被子类传进来了。那么在真正调用前,就会去获取 Authentication 对象和Collection 集合,然后进行 pre-invocation handling 操作。

后面会介绍 ConfigAttribute

如果调用时出现 AccessDecisionException,那么他将会被ExceptionTranslationFilter 处理。

在通过权限验证之后,就会准备一个 InterceptorStatusToken对象作为返回值。

在创建 token 之前,会尝试使用 RunAsManager 创建一个 Authentication对象,如果这个对象不为 null,那么就会把它放入一个SecurityContext,替换掉 SecurityContextHolder 中原有的那个。

原有的 SecurityContext 总是会被放到 token 中。

关于 RunAsManager :这里的逻辑是替换掉 SecurityContextHolder 中的值,这样在目标方法中看到的 Authentication 对象就是这个 RunAsManager 创建的对象。在目标方法调用完成后,即  finallyInvocation  方法中,会将原来的 SecurityContext 重新放回 SecurityContextHolder 中。

这样的目的是为了将认证与鉴权流程中的 Authentication 对象与业务方法中的区分开来。

在上面的这些步骤中,还会发出一些 ApplicationEvent,包括:PublicInvocationEventAuthorizationFailureEventAuthorizedEvent

PublicInvocationEvent 只在 Collection 为空的时候才会发生,而且这种时候不会调用 AccessDecisionManager

afterInfocation 方法

afterInvocation 方法主要目的是为了根据 returnedObject进行权限验证,这使用到了 AfterInvocationManager这个接口,这是在概览里没有提到的,它被用来进行 after invocation handling。

在这个方法中,如果有必要的话,就会使用 AfterInvocationManager.decide()方法来处理 returnedObject,得到一个新的结果作为 returntedObject

这里的有必要是指:

  1. token != null
  2. afterInvocationManager 字段不为空
finallyInfocation 方法

这个方法接收 InterceptorStatusToken 作为参数,只做一件事情:将 token 中的 SecurityContext 对象放回 SecurityContextHolder 中。

这个操作有两个判断条件:

  • token 不为 null

  • token 的 contextHolderRefreshRequiredtrue。当SecurityContextHolder 中的值在 beforeInvocation中被替换时,这个值才为 true


权限验证的入口 FilterSecurityInterceptor的介绍就到这里,接下来我们来看看 pre-invocation handling 和 after invocation handling 的内容,也就是 AccessDecisionManagerAfterInvocationManager

AccessDecisionManager

这是在概览中介绍过的内容,这里可以快速的回顾一下。

7f6b2f25e0e9cd54d3e96bc34262be9f.png

AccessDecisionManager 是 pre-invocation handling 的入口。它的三个具体实现会调用多个 AccessDecisionVoter的实现,然后具体实现的策略来决定如何根据 voter 的结果来判断是否通过身份验证。每一个 voter 都会根据当前的Authentication 对象、secureObjectCollection来做出是否允许访问的选择。

AccessDecisionManager 的三个实现,其实就是三种根据 voter 结果来决定最终结果的策略,分别是 AffirmativeBasedConsensusBasedUnanimousBased。策略顾名思义,就不解释了。

AfterInvocationManager

之前没有讲 after invocation handling 的部分,是觉得不重要,使用场景不多(其实是自己没遇到)。现在想讲一讲,是因为发现spring-security-acl 使用到了 after invocation handling 的机制。那么我们就来看看 AfterInvocationManager 是怎么工作的。

acl 的部分涉及一些新的概念,准备单独写一篇。

80cd143ba5abe5db7de17c76aaef506f.png

通过这个图,我们可以清楚的了解到,AfterInvocationManager也只是一个接口。它的实现 AfterInvocationProviderManager则是管理了“很多的” AfterInvocationProvider来真正的执行权限验证的操作。

这里“很多的” AfterInvocationProvider 其实也就只有四个实现,其中三个都是 acl,包括图里的这两个。

剩下的那个 PostInvocationAdviceProvider 其实也没有真正进行 authorization 操作,而是代理给了 PostInvocationAuthorizationAdvice处理。而这个 PostInvocationAuthorizationAdvice 也只有ExpressionBasedPostInvocationAdvice 这一个实现,也就是基于 SpEL表达式来进行 authorization 的实现。

而上面提到的所有的 manager 和 provider,都提供了 decide方法用来做权限验证。与 AccessDecisionManager.decide()相比,这些方法多了一个 returnedObject 参数。这既是因为它需要作为判断条件参与到决策过程中,也是因为它可能会在决策过程中被处理,然后返回一个新的returnedObject 作为处理后的结果。

ConfigAttribute

这个类是用来存储我们的 Security 的配置的。

举个例子,下面的代码就会生成相应的 ConfigAttribute

@Override
protected void configure(HttpSecurity http) throws Exception {
  http.authorizeRequests()
      .mvcMatchers("hello")
      .hasAuthority("test")
      .anyRequest()
      .authenticated();
}

上面的代码定义了:

  • 访问 /hello 的请求需要具有 test 权限

  • 其他任意请求,需要通过身份验证(不允许匿名访问)

这样我们就能得到这样的 ConfigAttribute

0078c69d8721342b9b66057f6fb8bdff.png

这是 FilterSecurityInterceptor 的截图。其中的securityMetadataSource 存储了很多的 ConfigAttributeAbstractSecurityInterceptor 通过子类实现的obtainSecurityMetadataSource 方法获取到它,然后通过它获取到本次使用的Collection

截图中的 requestMap 保存了 RequestMatcherCollection 的关系。

当我们请求 /hello 时,就会得到第一个Collection,也就是包含了 hasAuthority('test')的那一个。当我们请求其他接口时,就会得到第二个。

接着,这些被获取到的 ConfigAttribute 就可以被后续的验证逻辑使用到。

总结

本文介绍了 Spring Security Authorization,并着重介绍了FilterSecurityInterceptor 如何在 SecurityFilterChain 的最后使用AccessDecisionManagerAfterInvocationManager 来实现 pre-invocation handling 和 after invocation handling。

对于 AccessDecisionManagerAfterInfocationManager,则没有详细介绍内部的逻辑,而是介绍了它们如何利用子类和其他接口来完成权限验证的。其内部具体的细节逻辑,读者可以自己研究。

?,阅读原文,参与评论,查看更多文章

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值