欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

史上最简单的Spring Security教程(十九):AccessDecisionVoter简介及自定义访问权限投票器

程序员文章站 2022-06-02 23:29:13
...

 

为了后续对 AccessDecisionManager 的介绍,我们先来提前对 AccessDecisionVoter 做个简单的了解,然后,在捎带手自定义一个 AccessDecisionVoter

AccessDecisionVoter 的注释介绍如下:

Indicates a class is responsible for voting on authorization decisions. The coordination of voting (ie polling {@code AccessDecisionVoter}s, tallying their responses, and making the final authorization decision) is performed by an {@link org.springframework.security.access.AccessDecisionManager}.

什么意思呢?

翻译过来就是,AccessDecisionVoter 是一个投票器,负责对授权决策进行表决。然后,最终由唱票者AccessDecisionManager 统计所有的投票器表决后,来做最终的授权决策。

先来看几种常用的投票器。

 

WebExpressionVoter

 

最常用的,也是 Spring Security 框架默认 FilterSecurityInterceptor 实例中 AccessDecisionManager 默认的投票器 WebExpressionVoter。其实,就是对使用 http.authorizeRequests() 基于 Spring-EL进行控制权限的的授权决策类。

http
    .authorizeRequests()
    .anyRequest()
    .authenticated()
    .antMatchers().permitAll()
    .antMatchers().hasRole()
    .antMatchers().hasAuthority()
    ......

 

他还需要一个 ExpressionHandler,感兴趣的话,可以去看一下文章史上最简单的Spring Security教程(十七):FilterSecurityInterceptor默认初始化逻辑剖析里面有对 WebExpressionVoter  ExpressionHandler 的相关介绍。

 

 

AuthenticatedVoter


针对 ConfigAttribute#getAttribute() 中配置为 IS_AUTHENTICATED_FULLY、IS_AUTHENTICATED_REMEMBERED、IS_AUTHENTICATED_ANONYMOUSLY 权限标识时的授权决策。因此,其投票策略比较简单:

public int vote(Authentication authentication, Object object,
      Collection<ConfigAttribute> attributes) {
    int result = ACCESS_ABSTAIN;
​
    for (ConfigAttribute attribute : attributes) {
      if (this.supports(attribute)) {
        result = ACCESS_DENIED;
​
        if (IS_AUTHENTICATED_FULLY.equals(attribute.getAttribute())) {
          if (isFullyAuthenticated(authentication)) {
            return ACCESS_GRANTED;
          }
        }
​
        if (IS_AUTHENTICATED_REMEMBERED.equals(attribute.getAttribute())) {
          if (authenticationTrustResolver.isRememberMe(authentication)
              || isFullyAuthenticated(authentication)) {
            return ACCESS_GRANTED;
          }
        }
​
        if (IS_AUTHENTICATED_ANONYMOUSLY.equals(attribute.getAttribute())) {
          if (authenticationTrustResolver.isAnonymous(authentication)
              || isFullyAuthenticated(authentication)
              || authenticationTrustResolver.isRememberMe(authentication)) {
            return ACCESS_GRANTED;
          }
        }
      }
    }
​
    return result;
  }
}

 

PreInvocationAuthorizationAdviceVoter

 

用于处理基于注解 @PreFilter 和 @PreAuthorize 生成的 PreInvocationAuthorizationAdvice,来处理授权决策的实现。还记得我们最早使用 @PreAuthorize 来进行权限控制的介绍吗?史上最简单的Spring Security教程(十二):@PreAuthorize注解实现权限控制

public int vote(Authentication authentication, MethodInvocation method,
                Collection<ConfigAttribute> attributes) {
​
    // Find prefilter and preauth (or combined) attributes
    // if both null, abstain
    // else call advice with them
​
    PreInvocationAttribute preAttr = findPreInvocationAttribute(attributes);
​
    if (preAttr == null) {
        // No expression based metadata, so abstain
        return ACCESS_ABSTAIN;
    }
​
    boolean allowed = preAdvice.before(authentication, method, preAttr);
​
    return allowed ? ACCESS_GRANTED : ACCESS_DENIED;
}

 

RoleVoter

 

角色投票器。用于 ConfigAttribute#getAttribute() 中配置为角色的授权决策。其默认前缀为 ROLE_,可以自定义,也可以设置为空,直接使用角色标识进行判断。这就意味着,任何属性都可以使用该投票器投票,也就偏离了该投票器的本意,是不可取的。

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) {
        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;
}

 

注意,决策策略比较简单,用户只需拥有任一当前请求需要的角色即可,不必全部拥有

 

 

RoleHierarchyVoter

 

基于 RoleVoter,唯一的不同就是该投票器中的角色是附带上下级关系的。也就是说,角色A包含角色B,角色B包含 角色C,此时,如果用户拥有角色A,那么理论上可以同时拥有角色B、角色C的全部资源访问权限。

/**
 * Returns an array of all reachable authorities.
 * <p>
 * Reachable authorities are the directly assigned authorities plus all authorities
 * that are (transitively) reachable from them in the role hierarchy.
 * <p>
 * Example:<br>
 * Role hierarchy: ROLE_A &gt; ROLE_B and ROLE_B &gt; ROLE_C.<br>
 * Directly assigned authority: ROLE_A.<br>
 * Reachable authorities: ROLE_A, ROLE_B, ROLE_C.
 *
 * @param authorities - List of the directly assigned authorities.
 * @return List of all reachable authorities given the assigned authorities.
 */
public Collection<? extends GrantedAuthority> getReachableGrantedAuthorities(
    Collection<? extends GrantedAuthority> authorities);

 

注意,同 RoleVoter 的决策策略,用户只需拥有任一当前请求需要的角色即可,不必全部拥有

以上就是常用的投票器,接下来,我们自定义一个投票器,与 RoleVoter 策略正好相反,必须拥有当前请求所需的全部角色才能访问。尝试一下。

@Override
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) {
        if (this.supports(attribute)) {
            result = ACCESS_DENIED;
​
            // Attempt to find all matching granted authority
            for (GrantedAuthority authority : authorities) {
                if (attribute.getAttribute().equals(authority.getAuthority())) {
                    result = ACCESS_GRANTED;
                    break;
                }
            }
​
            if (result == ACCESS_DENIED) {
                return ACCESS_DENIED;
            }
        }
    }
​
    return result;
}

 

其它逻辑包括 rolePrefix、supports等,与 RoleVoter完全一致,只修改决策部分的逻辑:只要当前请求所需任一角色不在用户拥有的角色范围内,即代表没有授权,拒绝访问。

其它详细源码,请参考文末源码链接,可自行下载后阅读。

 

 

源码


github

 

https://github.com/liuminglei/SpringSecurityLearning/tree/master/19

 

gitee

 

https://gitee.com/xbd521/SpringSecurityLearning/tree/master/19

 

 


 

史上最简单的Spring Security教程(十九):AccessDecisionVoter简介及自定义访问权限投票器

回复以下关键字,获取更多资源

 

SpringCloud进阶之路 | Java 基础 | 微服务 | JAVA WEB | JAVA 进阶 | JAVA 面试 | MK 精讲

史上最简单的Spring Security教程(十九):AccessDecisionVoter简介及自定义访问权限投票器

 

 

 

 

笔者开通了个人微信公众号【银河架构师】,分享工作、生活过程中的心得体会,填坑指南,技术感悟等内容,会比博客提前更新,欢迎订阅。

技术资料领取方法:关注公众号,回复微服务,领取微服务相关电子书;回复MK精讲,领取MK精讲系列电子书;回复JAVA 进阶,领取JAVA进阶知识相关电子书;回复JAVA面试,领取JAVA面试相关电子书,回复JAVA WEB领取JAVA WEB相关电子书。

史上最简单的Spring Security教程(十九):AccessDecisionVoter简介及自定义访问权限投票器

 

相关标签: Web安全