spring-security 如何使用OPENID在资源服务器中管理和实现业务上下文角色?(SpringBoot和Keycloak)

ekqde3dh  于 2022-11-11  发布在  Spring
关注(0)|答案(2)|浏览(233)

我目前正在实现一个系统,将Sping Boot 作为资源服务器,将Keycloak作为授权服务器。我希望能够为用户创建不同的角色,但这应该会自动发生,因此没有管理员管理Keycloak后端。
我已经阅读了有关Keycloak管理API的内容,但我对这种方法非常不确定,因为这意味着将登录凭据存储在我的资源服务器中,这根本不是最佳实践,我想。所以我想储存(业务上下文)角色,但不为每个端点实现自己的授权方法。我希望能够将存储在资源服务器中的角色与Spring Security 一起使用,例如hasRole()方法,而且还包括OPEN ID身份验证(从授权中移交角色)已经使用的所有其他功能服务器上。
那么,我该怎么做呢?是否有一种安全的方法可以从资源服务器更新授权服务器中的用户角色?这对我来说似乎不太可能,因为(尽管在我的情况下不是这样)授权服务器可能不属于资源所有者。
或者,作为另一种方法,是否可以将从keycloak传递给 Boot 的角色与存储在资源服务器数据库中的角色融合在一起,并在通过keycloak对用户进行身份验证后查找它们?
我会非常高兴的一些代码片段,但指针的教程解释的主题是一样赞赏。

vatpfxk5

vatpfxk51#

所以,在谷歌上搜索了几次之后,我找到了一个完美的解决方案。这里有文档:https://www.baeldung.com/spring-security-map-authorities-jwt,但实际上我不能做很多。
有一个关于stackoverflow Spring Security: mapping OAuth2 claims with roles to secure Resource Server endpoints的答案,运行得很好。但也有我的源代码。

private class CustomJwtAuthenticationConverter implements Converter<Jwt, AbstractAuthenticationToken> {

    @Override
    public AbstractAuthenticationToken convert(final Jwt source) {

        Collection<GrantedAuthority> authorities = new ArrayList<>();
        authorities.add(new SimpleGrantedAuthority("ROLE_user"));

        // Get a unique id from jwt and search for the user in your database
        String suid = "";
        if(source.hasClaim("sub"))
            suid = source.getClaim("sub");
        else
            return new JwtAuthenticationToken(source, authorities);

        Person person = personRepository.findBySuid(suid);

        // Set any role you want, I have not tried but I am sure it also works 
        // with permissions 
        if ( person != null && person.getRole().equals("admin") )
            authorities.add(new SimpleGrantedAuthority("ROLE_admin"));
        else
            return new JwtAuthenticationToken(source, authorities);

        return new JwtAuthenticationToken(source, authorities);
    }
}

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception{
    http     // Remove h2 setup for production
            .authorizeRequests().antMatchers("/h2-console/**").permitAll()
            .and().csrf().ignoringAntMatchers("/h2-console/**")
            .and().headers().frameOptions().sameOrigin()

            // Above is only for h2 console
            .and().cors().and().csrf().disable()
            .authorizeRequests()
            .antMatchers("/api/heroes/**").hasRole("admin")       
            .anyRequest().authenticated()
            .and()
            .oauth2ResourceServer()
            .jwt()
            .jwtAuthenticationConverter(new CustomJwtAuthenticationConverter());
    return http.build();
}

您可以通过任何方式为经过身份验证的用户计算角色,例如使用JPA或存储用户数据的其他数据源。整个JWT令牌将交给您,正如您所看到的,我使用的是“子”密钥,但是对于不同的授权服务器实现,您应该自己选择唯一标识符。
毕竟,我选择了资源服务器持久角色管理。我认为这是一个很好的实践,因为业务上下文应该存储在那里。我希望我可以帮助每个有同样问题的人。

7jmck4yq

7jmck4yq2#

我不太同意你的看法:在我看来,授权服务器是管理身份验证和授权数据的最佳位置。
我更喜欢为授权服务器提供“插件”(Keycloak中所谓的Map器),以便使用我需要的私有声明来丰富访问令牌,并且在资源服务器上,仅从访问令牌声明构建Spring安全上下文
这意味着:

  • 资源-服务器访问授权-服务器通过管理API更新用户角色(使用客户端凭据)
  • 授权服务器在构建访问令牌时使用Web服务来检索一些额外数据(例如,他本机不处理的一些安全属性)

最后,这也会导致定义一个自定义身份验证转换器,但每次生成访问令牌(由授权服务器将其添加到访问令牌中)时,DB访问(检索角色或其他内容)只发生一次,而不是每次请求(由资源服务器构建安全上下文)时发生一次。

编辑

我没有资源服务器调用授权服务器管理API的示例,但这只是一个问题:

  • 使用REST客户端(WebClientRestTemplate@FeignClient或任何您喜欢的客户端),该客户端能够使用客户端凭据来获取访问令牌
  • 使用“机密”客户端配置授权服务器,并为其激活客户端凭证流。根据您的授权服务器,您可能需要为该客户端授予某些角色,以允许其使用所需的管理API部分。

我有一个例子,Keycloak联系一个资源服务器,添加一个私有声明,其中一些安全数据不是它本机处理的(用户之间的角色委派)。这是用this Keycloak "mapper"完成的。私有声明在资源服务器上由一个自定义的JwtAuthenticationConverter使用:在这个项目中,我没有使用JwtAuthenticationToken作为Authentication,而是使用一个自定义值:ProxiesAuthentication(它包含负责解析私有声明的ProxiesClaimSet)。
本示例中的流程是授权服务器调用资源服务器,因为Keycloak不知道“用户代理”,也没有为它公开API。但结果是相同的:所有的安全数据都包含在令牌中,使得资源服务器不需要昂贵的操作(DB访问或web服务调用)来建立安全上下文。

相关问题