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 <code>AccessDecisionVoter</code> returns an affirmative response.
 */

 

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

再看一下其决策逻辑。

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

    // To get this far, every AccessDecisionVoter abstained
    checkAllowIfAllAbstainDecisions();
}

 

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

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

 

ConsensusBased 

 

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

public void decide(Authentication authentication, Object object,
      Collection<ConfigAttribute> 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<ConfigAttribute> attributes) throws AccessDeniedException {

    int grant = 0;

    List<ConfigAttribute> 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<ConfigAttribute> attributes) throws AccessDeniedException {
    Collection<? extends GrantedAuthority> 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 匹配,即代表授予访问权限。