一、授权流程
Spring Security
可以通过 http.authorizeRequests()
对 web
请求进行授权保护。 Spring Security
使用标准 Filter
建立了对 web
请求的拦截,最终对资源的授权访问。
Spring Security
的授权流程如下:
分析授权流程:
-
拦截请求,已认证用户访问受保护的
web
资源被SpringFilterChain
中的FilterSecurityInterceptor
的子类拦截。 -
获取资源访问策略,
FilterSecurityInterceptor
会从SecurityMetadataSource
的子类DefaultFilterInvocationSecurityMetadataSource
获取要访问当前资源所需要的权限Collection<configattribute></configattribute>
。
SecurityMetadataSource
读取访问策略的抽象,而读取的内容,就是我们配置的访问规则,读取的访问策略如:
http
.authorizeRequests() .antMatchers("/r/r1").hasAuthority("p1")
.antMatchers("/r/r2").hasAuthority("p2")
...
FilterSecurityInterceptor
会调用AccessDecisionManager
进行授权决策,若决策通过,则允许访问资 源,否则将禁止访问。
1.1 AccessDecisionManager
AccessDecisionManager
接口定义:
public interface AccessDecisionManager {
void decide(Authentication var1, Object var2,
Collection<ConfigAttribute> var3)
throws AccessDeniedException,
InsufficientAuthenticationException;
boolean supports(ConfigAttribute var1);
boolean supports(Class<?> var1);
}
decide()
方法是 AccessDecisionManager
的核心, 用来鉴定当前用户是否有访问对应受保护资源的权限,其中参数:
var1
:要访问资源的访问者的身份
var2
:要访问的受保护资源,web请求对应 FilterInvocation
var3
:是受保护资源的访问策略,通过 SecurityMetadatasource
1.2 授权决策
AccessDecisionManager
采用投票的方式来确定是否能够访问受保护资源。
通过上图可以看出, AccessDecisionManager
中包含了一系列 AccessDecisionVoter
将会被用来对 Authentication
是否有权访问受保护对象进行投票, AccessDecisionManager
根据投票结果,做出最终决策。
AccessDecisionVoter
接口定义如下:
public interface AccessDecisionVoter<S> {
int ACCESS_GRANTED = 1;
int ACCESS_ABSTAIN = 0;
int ACCESS_DENIED = -1;
boolean supports(ConfigAttribute var1);
boolean supports(Class<?> var1);
int vote(Authentication var1, S var2,
Collection<ConfigAttribute> var3);
}
vote()
的返回结果会是 AccessDecisionVoter
中定义的三个常量之一。 ACCESS_GRANTED
表示同意, ACCESS_DENIED
表示拒绝, ACCESS_ABSTAIN
表示弃权。如果 AccessDecisionVoter
不能判定当前 Authentication
是否拥有访问对应受保护对象的权限,则 vote()
方法的返回值为 ACCESS_ABSTAIN
。
Spring Security
内置了三个基于投票的 AccessDecisionManager
实现类如下,它们分别是 AffirmativeBased
、 ConsensusBased
和 UnaimousBased
。 Spring Security
默认使用的是 AffirmativeBased
。
1、AffirmativeBased:
①只要有 AccessDecisionVoter
的投票为 ACCESS_GRANTED
则同意用户进行访问;
②如果全部弃权也表示通过;
③如果没有一个投赞成票,但是有人投反对票,则将抛出 AccessDeniedException
。
2、ConsensusBased:
①如果赞成票多余反对票则表示通过;
②如果反对票多于赞成票则将抛出 AccessDeniedException
。
③如果赞成票与反对票相同且不等于0,并且属性 allowEqualGrantedDeniedDecision
的值为true,则表示通过,否则将抛出 AccessDeniedException
。 allowEqualGrantedDeniedDecision
的默认值为true。
④如果所有的 AccessDecisionVoter
都弃权了,则将视参数 allowIfAllAbstainDecisions
的值而定,如果该值为true则表示通过,否则将抛出异常 AccessDeniedException
。参数 allowIfAllAbstainDecisions
的值默认为false。
3、UnanimousBased:
UnanimousBased
的逻辑与另外两种实现有点不一样,另外两种会一次性把受保护对象的配置属性全部传递给 AccessDecisionVoter
进行投票,而 UnanimousBased
会一次只传递一个 ConfigAttribute
给 AccessDecisionVoter
进行投票。这也就意味着如果我们的 AccessDecisionVoter
的逻辑是只要传递进来的 ConfigAttribute
中有一个能够匹配则投赞成票,但是放到 UnanimousBased
中其投票结果就不一定是赞成了。 UnanimousBased的逻辑具体来说是这样的:
①如果受保护对象配置的某一个 ConfigAttribute
被任意的 AccessDecisionVoter
反对了,则将抛出 AccessDeniedException
。
②如果没有反对票,但是有赞成票,则表示通过。
③如果全部弃权了,则将视参数 allowIfAllAbstainDecisions
的值而定,true则通过,false则抛出 AccessDeniedException
。
Spring Security
也内置一些投票者实现类如 RoleVoter
、 AuthenticatedVoter
和 WebExpressionVoter
等。