文章目录
一、授权流程
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等。