### 文章目录
[toc]
# 一、授权流程
`Spring Security`可以通过 `http.authorizeRequests()`对 `web`请求进行授权保护。 `Spring Security`使用标准 `Filter`建立了对 `web`请求的拦截,最终对资源的授权访问。
`Spring Security`的授权流程如下:
![](https://img-blog.csdnimg.cn/20200406235818944.png)
分析授权流程:
1. **拦截请求**,已认证用户访问受保护的 `web`资源被 `SpringFilterChain`中的 `FilterSecurityInterceptor`的子类拦截。
2. **获取资源访问策略**, `FilterSecurityInterceptor`会从 `SecurityMetadataSource`的子类 `DefaultFilterInvocationSecurityMetadataSource`获取要访问当前资源所需要的权限 `Collection<configattribute></configattribute>`。
`SecurityMetadataSource`读取访问策略的抽象,而读取的内容,就是我们配置的访问规则,读取的访问策略如:
```java
http
.authorizeRequests() .antMatchers("/r/r1").hasAuthority("p1")
.antMatchers("/r/r2").hasAuthority("p2")
...
```
3. `FilterSecurityInterceptor`会调用 `AccessDecisionManager`进行授权决策,若决策通过,则允许访问资 源,否则将禁止访问。
## 1.1 AccessDecisionManager
`AccessDecisionManager`接口定义:
```java
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`采用投票的方式来确定是否能够访问受保护资源。
![](https://img-blog.csdnimg.cn/20200407224236206.png)
通过上图可以看出, `AccessDecisionManager`中包含了一系列 `AccessDecisionVoter`将会被用来对 `Authentication`是否有权访问受保护对象进行投票, `AccessDecisionManager`根据投票结果,做出最终决策。
`AccessDecisionVoter`接口定义如下:
```java
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`等。
来源:oschina
链接:https://my.oschina.net/u/4199331/blog/4873706