史上最简单的Spring Security教程(二十一):AccessDecisionManager简介及自定义访问决策管理器

 

AccessDecisionManager 顾名思义,访问决策管理器。

Makes a final access control (authorization) decision

做出最终的访问控制(授权)决定。

而常用的 AccessDecisionManager 有三个,分别介绍一下其权限决策逻辑。

 

AffirmativeBased

 

Spring Security 框架默认的 AccessDecisionManager。

/**
 * Simple concrete implementation of
 * {@link org.springframework.security.access.AccessDecisionManager} that grants access if
 * any AccessDecisionVoter returns an affirmative response.
 */

 

从其注释能看出,只要任一 AccessDecisionVoter 返回肯定的结果,便授予访问权限

再看一下其决策逻辑。

public void decide(Authentication authentication, Object object,
      Collection 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"));
    }
​
    // To get this far, every AccessDecisionVoter abstained
    checkAllowIfAllAbstainDecisions();
}

 

其决策逻辑也比较容易看懂,所有当前请求需要的 ConfigAttributes ,全部交给 AccessDecisionVoter 进行投票。只要任一 AccessDecisionVoter 授予访问权限,便返回,不再继续决策判断。否则,便抛出访问授权拒绝的异常,即:AccessDeniedException

注意,最后是根据配置,判断如果全部 AccessDecisionVoter 都授权决策中立时的授权决策。

 

ConsensusBased 

 

少数服从多数授权访问决策方案

public void decide(Authentication authentication, Object object,
      Collection configAttributes) throws AccessDeniedException {
    int grant = 0;
    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:
                grant++;
​
                break;
​
            case AccessDecisionVoter.ACCESS_DENIED:
                deny++;
​
                break;
​
            default:
                break;
        }
    }
​
    if (grant > deny) {
        return;
    }
​
    if (deny > grant) {
        throw new AccessDeniedException(messages.getMessage(
            "AbstractAccessDecisionManager.accessDenied", "Access is denied"));
    }
​
    if ((grant == deny) && (grant != 0)) {
        if (this.allowIfEqualGrantedDeniedDecisions) {
            return;
        }
        else {
            throw new AccessDeniedException(messages.getMessage(
                "AbstractAccessDecisionManager.accessDenied", "Access is denied"));
        }
    }
​
    // To get this far, every AccessDecisionVoter abstained
    checkAllowIfAllAbstainDecisions();
}

 

少数服从多数判断逻辑,并不需要过多的解释。只需要注意一点,那就是授予权限和拒绝权限相等时的逻辑其实,该决策器也考虑到了这一点,所以提供了 allowIfEqualGrantedDeniedDecisions 参数,用于给用户提供自定义的机会,其默认值为 true,即代表允许授予权限和拒绝权限相等,且同时也代表授予访问权限

 

UnanimousBased 

 

最严格的的授权决策器。要求所有 AccessDecisionVoter 均返回肯定的结果时,才代表授予权限。

/**
 * Simple concrete implementation of
 * {@link org.springframework.security.access.AccessDecisionManager} that requires all
 * voters to abstain or grant access.
 */

 

其决策逻辑如下。

public void decide(Authentication authentication, Object object,
      Collection attributes) throws AccessDeniedException {
​
    int grant = 0;
​
    List singleAttributeList = new ArrayList<>(1);
    singleAttributeList.add(null);
​
    for (ConfigAttribute attribute : attributes) {
        singleAttributeList.set(0, attribute);
​
        for (AccessDecisionVoter voter : getDecisionVoters()) {
            int result = voter.vote(authentication, object, singleAttributeList);
​
            if (logger.isDebugEnabled()) {
                logger.debug("Voter: " + voter + ", returned: " + result);
            }
​
            switch (result) {
                case AccessDecisionVoter.ACCESS_GRANTED:
                    grant++;
​
                    break;
​
                case AccessDecisionVoter.ACCESS_DENIED:
                    throw new AccessDeniedException(messages.getMessage(
                        "AbstractAccessDecisionManager.accessDenied",
                        "Access is denied"));
​
                default:
                    break;
            }
        }
    }
​
    // To get this far, there were no deny votes
    if (grant > 0) {
        return;
    }
​
    // To get this far, every AccessDecisionVoter abstained
    checkAllowIfAllAbstainDecisions();
}

 

可以看到,同前两个决策器不同之处在于,循环将每一个当前请求需要的 ConfigAttribute 传递给 AccessDecisionVoter 进行决策,而不是全部传递过去。这就代表每一个  ConfigAttribute 每一个 AccessDecisionVoter 均需返回肯定的结果才可以授予权限。所以,最为严格。

 

 

自定义访问决策管理器 

 

既然了解了 AccessDecisionManager 的功用,那么我们就可以按照自身业务场景进行定制。比如前面提到的动态资源授权。我们假设用户拥有任一角色即可访问,那么所需的逻辑如下。

@Override
public void decide(Authentication authentication, Object object, Collection attributes) throws AccessDeniedException {
    Collection authorities = extractAuthorities(authentication);
​
    for (ConfigAttribute attribute : attributes) {
        if (this.supports(attribute)) {
            // Attempt to find a matching granted authority
            for (GrantedAuthority authority : authorities) {
                if (attribute.getAttribute().equals(authority.getAuthority())) {
                    return;
                }
            }
​
            logger.warn("current user not have the '{}' attribute.", attribute);
        }
    }
​
    logger.warn("current request should be have at least one of the attributes {}.", attributes);
​
    throw new AccessDeniedException("Access is denied.");
}

 

我们把当前请求所需的所有 ConfigAttribute 传递给 AccessDecisionVoter 进行决策,只要任一与用户拥有 GrantedAuthority 匹配,即代表授予访问权限。

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

 

 

源码

 

github

 

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

 

gitee

 

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

 

 

 

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

 

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

 

 

 

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

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

 

你可能感兴趣的:(Web安全)