史上最简单的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 <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 匹配,即代表授予访问权限。

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

 

 

源码

 

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相关电子书。

 

  • 5
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
Spring Security提供了多种方式来实现自定义权限管理。其中一种方式是通过自定义AccessDecisionManagerSecurityMetadataSource来实现。 AccessDecisionManager用于决定用户是否有权限访问某个资源。你可以实现自己的AccessDecisionManager来根据自定义的规则进行权限判断。例如,你可以基于用户的角色或权限来判断用户是否有权限访问某个接口或页面。 SecurityMetadataSource用于提供资源与权限的映射关系。你可以实现自己的SecurityMetadataSource来定义资源与权限的对应关系。例如,你可以将接口或页面与相应的权限进行映射。 在配置类中,你可以使用withObjectPostProcessor方法来设置自定义AccessDecisionManagerSecurityMetadataSource。例如: ```java @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers("/login","/logout").permitAll() .anyRequest().authenticated() .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() { @Override public <O extends FilterSecurityInterceptor> O postProcess(O o) { o.setAccessDecisionManager(customAccessDecisionManager); o.setSecurityMetadataSource(customSecurityMetadataSource); return o; } }); } ``` 在上述配置中,customAccessDecisionManager和customSecurityMetadataSource分别是你自定义AccessDecisionManagerSecurityMetadataSource的实例。 通过这种方式,你可以根据自己的需求来实现更加灵活和个性化的权限管理。 #### 引用[.reference_title] - *1* [SpringSecurity自定义权限](https://blog.csdn.net/qq_62770345/article/details/129089689)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [springsecurity自定义角色权限授权](https://blog.csdn.net/mingpingyao/article/details/129054262)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值