Spring 安全全局预授权规则

Spring security global preauthorize rule

我正在研究一种让网关后面的服务轻松通信的方法。

该计划使用 oauth 令牌中的范围,该范围将由同一系统上的服务 运行 使用。

例如。如果 oauth 令牌具有范围 'acct' 和 'user',则下面的 api 方法将可用。在不更改注释的情况下,如果 ouath 令牌具有范围 'admin' 并且缺少范围 'acct' 和 'user'.

中的一个或两个范围,我希望它也可用
@PreAuthorize("#oauth2.hasScope('acct') and #oauth2.hasScope('user')")
@RequestMapping(value = "/scopedtest", produces = "application/json")
public Map<String, String> indexWithScope() {
    return getHashMapResult();
}

我希望这个 'admin' 范围被全球接受,这样服务开发人员就不需要在他们制作的每个 api 控制器上包含管理范围,但仍然允许内部服务访问 api其他内部服务。

这将用作拦截预授权调用的代码,并将响应更改为调用者已获得授权(如果具有 'admin' 范围)。如果 oauth 令牌具有预授权注释所需的范围,则调用将正常运行。

使用网络安全

您当前正在对控制器操作使用预授权规则。方法安全性最初的强大之处在于,在与路由直接相关的 而非 方法上使用它,因为这些可以设置 HttpSecurity 请求匹配器,通常使用 WebSecurityConfigurerAdapter.

然而,这并不能真正帮助您,因为 spring 有一个路由权限方法,而不是权限路由方法。这意味着,如果您拥有一个全局角色,无论具体配置如何,都可以访问所有内容,那么如果您要避免更改默认配置,则必须将其配置为所有安全定义。

我认为解决您需求的唯一方法是挂接到 spring 的安全过滤过程并为管理范围定义一个例外规则。您可以阅读 here 关于如何实现自定义过滤器的信息,您可以在提取 oauth2 身份验证后放置该过滤器以查找管理范围(或不同的全局规则)

树角色

这是我在我的项目中使用的一些合乎逻辑的解决方案,它为角色提供了一些 "parent" 角色,这暗示了树中当前节点下的角色。因此 root 隐式授予您配置的所有角色/范围。为简单起见,我在写入时应用这些树逻辑,例如给 [ROOT_ROLE] 结果添加(树遍历)下的所有角色或撤销通过从节点向上到根的深度优先搜索找到的所有角色。动机就在这里,访问控制策略通常反映用户的层次结构,并提供更多自由来使用树正确设计这些个人层次结构。此模式以可扩展的方式间接解决了您的问题

已通过向现有决策投票者列表添加投票者解决此问题。

第一步是创建自定义投票器class

public class CustomVoter implements AccessDecisionVoter {

    @Override
    public boolean supports(ConfigAttribute attribute) {
        return true;
    }

    @Override
    public int vote(Authentication authentication, Object object, Collection collection) {
        //Place your decision code here
        if( check_is_true() ) {
            //grant access
            return ACCESS_GRANTED;
        } else if ( check_is_false() ) {
            //deny access
            return ACCESS_DENIED;
        } else {
            //do not make a choice
            return ACCESS_ABSTAIN;
        }
    }

    @Override
    public boolean supports(Class clazz) {
        return true;
    }
}

我们现在需要将此选民添加到将做出访问决定的选民列表中。

@Configuration
public class DecisionVotersConfiguration {

    @Autowired
    MethodInterceptor methodSecurityInterceptor;

    @PostConstruct
    @DependsOn("methodSecurityInterceptor")
    public void modifyAccessDecisionManager() {
        ((AffirmativeBased)((MethodSecurityInterceptor)methodSecurityInterceptor).getAccessDecisionManager()).getDecisionVoters().add(0, new CustomVoter());
    }
}

这会将您的自定义决策选民添加到决策选民列表中。通过将它放在索引 0 处,它将首先被检查。这将允许选民在以后的检查拒绝访问之前授予访问权限。此配置中的方法 class 将取决于正在创建的 methodSecurityInterceptor,它将具有决策选民的初始列表。