我们先来看下类图。顶层接口为 AccessDecisionManager,仅有一个抽象实现,三个具体实现类继承自抽象类。
- AffirmativeBased 一票通过制
- ConsensusBased 半数以上通过制
- UnanimousBased 全数通过制
见名知意,不用我多解释了。
在这几个决策器中调用投票器AccessDecisionVoter,进行投票。
RoleVoter投票器的逻辑如下:
public int vote(Authentication authentication, Object object,
Collection<ConfigAttribute> attributes) {
//先判断是否认证,未认证直接投拒绝票
if (authentication == null) {
return ACCESS_DENIED;
}
int result = ACCESS_ABSTAIN;
//从认证信息中获取用户拥有得权限信息
Collection<? extends GrantedAuthority> authorities = extractAuthorities(authentication);
//遍历访问需要得权限信息
for (ConfigAttribute attribute : attributes) {
//判断是否以ROLE_开头(根据不同的业务不同)
if (this.supports(attribute)) {
result = ACCESS_DENIED;
//遍历用户拥有得权限信息与访问所需得信息进行匹配
// Attempt to find a matching granted authority
for (GrantedAuthority authority : authorities) {
if (attribute.getAttribute().equals(authority.getAuthority())) {
return ACCESS_GRANTED;
}
}
}
}
return result;
}
投票器的三个常量
- ACCESS_ABSTAIN 0 弃权
- ACCESS_DENIED -1反对票
- ACCESS_GRANTED 1通过票
一次只能使用一个决策器,可以使用多个投票器。
来看决策器的源码
AffirmativeBased.java
public void decide(Authentication authentication, Object object,
Collection<ConfigAttribute> configAttributes) throws AccessDeniedException {
int deny = 0;
//循环配置的所有投票器
for (AccessDecisionVoter voter : getDecisionVoters()) {
int result = voter.vote(authentication, object, configAttributes);
if (logger.isDebugEnabled()) {
logger.debug("Voter: " + voter + ", returned: " + result);
}
//此处逻辑可以看出,只要有一票通过则通过
switch (result) {
case AccessDecisionVoter.ACCESS_GRANTED:
return;
case AccessDecisionVoter.ACCESS_DENIED:
deny++;
break;
default:
break;
}
}
if (deny > 0) {
throw new AccessDeniedException(messages.getMessage(
"AbstractAccessDecisionManager.accessDenied", "Access is denied"));
}
//如果所有都弃权了怎么办?在父类里面有个属性allowIfAllAbstainDecisions 配置为true 则算通过,如果为false 则不通过抛出拒绝访问异常。默认false
// To get this far, every AccessDecisionVoter abstained
checkAllowIfAllAbstainDecisions();
}
同理其它的决策器也是如此不多介绍。
那么如果不用默认配置,我们应该如何配置它呢?你只需要自定义AccessDecisionManager的实例化过程即可。如下配置了默认RoleVoter以及自定义Voter。然后将accessDecisionManager 配置给 http即可。
@Bean
public AccessDecisionManager accessDecisionManager(){
//此处无需用linkList 本身与顺序无关。
List<AccessDecisionVoter<? extends Object>> decisionVoters=new ArrayList<>();
AccessDecisionVoter roleVoter=new RoleVoter();
decisionVoters.add(roleVoter);
decisionVoters.add(customVoter);
return new AffirmativeBased(decisionVoters);
}
//代码片段其它内容自己加
public void configure(HttpSecurity http){
http.authorizeRequests().accessDecisionManager(accessDecisionManager());
}
也就是说,如果我们需要扩展,直接实现一个voter,然后将之注册到AccessDecisionManager中即可。
AbstractInterceptUrlConfigurer.getAccessDecisionManager中可以看到,security默认配置的就是AffirmativeBased 决策器
/**
* Creates the default {@code AccessDecisionManager}
* @return the default {@code AccessDecisionManager}
*/
private AccessDecisionManager createDefaultAccessDecisionManager(H http) {
AffirmativeBased result = new AffirmativeBased(getDecisionVoters(http));
return postProcess(result);
}