原文连接: https://blog.gaoyuexiang.cn/2020/06/13/spring-security-authorization/,
内容无差异。
在前面的文章里,咱们对 Spring Security
进行权限验证的组件有了大体的了解,咱们首先来回顾并探究一下细节。java
这是 AbstractSecurityInterceptor
的一个子类,而且实现了 Filter
接口,负责调用父类的 beforeInvocation()
、afterInvocatio()
和finallyInvocation()
方法以及一些 Servlet 相关的工做。
真正处理权限验证的代码,其实在父类中。 它存在的意义就是为了能在 Filter
中进行权限验证。spring
这个 Filter
默认老是被安排在 SecurityFilterChain
的最后,由于须要保证它在全部的身份认证相关的 Filter
以后。mvc
这个类实现了真正的权限验证的逻辑,它有多个子类,是为了适配不一样的技术而存在的,好比上面的FilterSecurityInterceptor
就是为了适配 Servlet Filter 而存在的。ide
咱们能够关注一下上面提到的三个方法,这是每一个子类都会调用的。ui
子类的实现老是下面的套路:spa
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);
secureObject
是一个方法调用,它的类型是 Object
,但通常会看到MethodInvocation
或者 FilterInvocation
这样的类型。这个方法的目标是调用 AccessDecisionManager.decide()
方法,完成
pre-invocation handling 操做。代理
在前面的link:/2020/05/31/spring-security-servlet-overview#_权限验证[概览]中介绍过,AccessDecisionManager.decide()
方法有三个参数。其中的 secureObject 已经被子类传进来了。
那么在真正调用前,就会去获取 Authentication
对象和Collection<ConfigAttribute>
集合,而后进行 pre-invocation handling
操做。code
后面会介绍
ConfigAttribute
若是调用时出现 AccessDecisionException
,那么他将会被ExceptionTranslationFilter
处理。对象
在经过权限验证以后,就会准备一个 InterceptorStatusToken
对象做为返回值。blog
在建立 token 以前,会尝试使用 RunAsManager
建立一个 Authentication
对象,若是这个对象不为 null
,那么就会把它放入一个SecurityContext
,替换掉 SecurityContextHolder
中原有的那个。
原有的 SecurityContext
老是会被放到 token 中。
关于RunAsManager
:这里的逻辑是替换掉SecurityContextHolder
中的值,这样在目标方法中看到的Authentication
对象就是这个RunAsManager
建立的对象。在目标方法调用完成后,即 link:#_finallyinfocation_方法[finallyInvocation 方法] 中,会将原来的SecurityContext
从新放回SecurityContextHolder
中。这样的目的是为了将认证与鉴权流程中的
Authentication
对象与业务方法中的区分开来。
在上面的这些步骤中,还会发出一些 ApplicationEvent
,包括:PublicInvocationEvent
、AuthorizationFailureEvent
和AuthorizedEvent
。
PublicInvocationEvent
只在Collection<ConfigAttribute>
为空的时候才会发生,并且这种时候不会调用AccessDecisionManager
。
afterInvocation
方法主要目的是为了根据 returnedObject
进行权限验证,这使用到了 AfterInvocationManager
这个接口,这是在概览里没有提到的,它被用来进行
after invocation handling。
在这个方法中,若是有必要的话,就会使用 AfterInvocationManager.decide()
方法来处理 returnedObject
,获得一个新的结果做为 returntedObject
。
这里的有必要是指:
- token != null
afterInvocationManager
字段不为空
这个方法接收 InterceptorStatusToken
做为参数,只作一件事情:将 token
中的 SecurityContext
对象放回 SecurityContextHolder
中。
这个操做有两个判断条件:
contextHolderRefreshRequired
为 true
。当SecurityContextHolder
中的值在 beforeInvocation
true
权限验证的入口 FilterSecurityInterceptor
的介绍就到这里,接下来咱们来看看 pre-invocation handling 和 after
invocation handling 的内容,也就是 AccessDecisionManager
与AfterInvocationManager
。
这是在概览中介绍过的内容,这里能够快速的回顾一下。
AccessDecisionManager
是 pre-invocation handling 的入口。
它的三个具体实现会调用多个 AccessDecisionVoter
的实现,而后具体实现的策略来决定如何根据 voter
的结果来判断是否经过身份验证。 每个 voter 都会根据当前的Authentication
对象、secureObject
和 Collection<ConfigAttribute>
来作出是否容许访问的选择。
AccessDecisionManager
的三个实现,其实就是三种根据 voter
结果来决定最终结果的策略,分别是 AffirmativeBased
、ConsensusBased
和UnanimousBased
。策略顾名思义,就不解释了。
以前没有讲 after invocation handling
的部分,是以为不重要,使用场景很少(实际上是本身没遇到)。如今想讲一讲,是由于发现spring-security-acl
使用到了 after invocation handling
的机制。那么咱们就来看看 AfterInvocationManager
是怎么工做的。
acl
的部分涉及一些新的概念,准备单独写一篇。
经过这个图,咱们能够清楚的了解到,AfterInvocationManager
也只是一个接口。 它的实现 AfterInvocationProviderManager
则是管理了“不少的” AfterInvocationProvider
来真正的执行权限验证的操做。
这里“不少的”AfterInvocationProvider
其实也就只有四个个实现,其中三个都是acl
,包括图里的这两个。
剩下的那个 PostInvocationAdviceProvider
其实也没有真正进行
authorization 操做,而是代理给了 PostInvocationAuthorizationAdvice
处理。 而这个 PostInvocationAuthorizationAdvice
也只有ExpressionBasedPostInvocationAdvice
这一个实现,也就是基于 SpEL
表达式来进行 authorization 的实现。
而上面提到的全部的 manager 和 provider,都提供了 decide
方法用来作权限验证。 与 AccessDecisionManager.decide()
相比,这些方法多了一个 returnedObject
参数。
这既是由于它须要做为判断条件参与到决策过程当中,也是由于它可能会在决策过程当中被处理,而后返回一个新的returnedObject
做为处理后的结果。
这个类是用来存储咱们的 Security 的配置的。
举个例子,下面的代码就会生成相应的 ConfigAttribute
:
@Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .mvcMatchers("hello") .hasAuthority("test") .anyRequest() .authenticated(); }
上面的代码定义了:
/hello
的请求须要具备 test
权限这样咱们就能获得这样的 ConfigAttribute
:
这是 FilterSecurityInterceptor
的截图。 其中的securityMetadataSource
存储了不少的 ConfigAttribute
。AbstractSecurityInterceptor
经过子类实现的obtainSecurityMetadataSource
方法获取到它,而后经过它获取到本次使用的Collection<ConfigAttribute>
。
截图中的 requestMap
保存了 RequestMatcher
与Collection<ConfigAttribute>
的关系。
当咱们请求 /hello
时,就会获得第一个Collection<ConfigAttribute>
,也就是包含了 hasAuthority('test')
的那一个。 当咱们请求其余接口时,就会获得第二个。
接着,这些被获取到的 ConfigAttribute
就能够被后续的验证逻辑使用到。
本文介绍了 Spring Security Authorization,并着重介绍了FilterSecurityInterceptor
如何在 SecurityFilterChain
的最后使用AccessDecisionManager
和 AfterInvocationManager
来实现
pre-invocation handling 和 after invocation handling。
对于 AccessDecisionManager
和AfterInfocationManager
,则没有详细介绍内部的逻辑,而是介绍了它们如何利用子类和其余接口来完成权限验证的。其内部具体的细节逻辑,读者能够本身研究。