带有 Keycloak 的 SpringBoot OAuth2 不返回映射角色作为权限

K. *_*ddy 10 java oauth-2.0 spring-boot keycloak

我正在创建一个简单的 SpringBoot 应用程序并尝试与 OAuth 2.0 提供程序 Keycloak 集成。我在领域级别创建了领域、客户端、角色(成员、PremiumMember),最后创建了用户并分配了角色(成员、PremiumMember)。

如果我使用 Keycloak https://www.keycloak.org/docs/latest/securing_apps/index.html#_spring_boot_adapter提供的 SpringBoot 适配器,那么当我成功登录并检查登录用户的权限时,我可以看到分配的角色例如会员、高级会员。

Collection<? extends GrantedAuthority> authorities = 
 SecurityContextHolder.getContext().getAuthentication().getAuthorities();
Run Code Online (Sandbox Code Playgroud)

但是,如果我使用通用 SpringBoot Auth2 客户端配置,我可以登录,但是当我检查权限时,它总是仅显示ROLE_USER、SCOPE_email、SCOPE_openid、SCOPE_profile ,并且不包括我映射的角色(Member、PremiumMember)。

我的 SpringBoot OAuth2 配置:

pom.xml

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
Run Code Online (Sandbox Code Playgroud)

应用程序属性

spring.security.oauth2.client.provider.spring-boot-thymeleaf-client.issuer-uri=http://localhost:8181/auth/realms/myrealm

spring.security.oauth2.client.registration.spring-boot-thymeleaf-client.authorization-grant-type=authorization_code
spring.security.oauth2.client.registration.spring-boot-thymeleaf-client.client-id=spring-boot-app
spring.security.oauth2.client.registration.spring-boot-thymeleaf-client.client-secret=XXXXXXXXXXXXXX
spring.security.oauth2.client.registration.spring-boot-thymeleaf-client.scope=openid,profile,roles
spring.security.oauth2.client.registration.spring-boot-thymeleaf-client.redirect-uri=http://localhost:8080/login/oauth2/code/spring-boot-app
Run Code Online (Sandbox Code Playgroud)

我正在使用SpringBoot 2.5.5和Keycloak 15.0.2。

使用这种通用的 OAuth2.0 配置方法(不使用 Keycloak SpringBootAdapter)有没有办法获取分配的角色?

Tho*_*ale 34

默认情况下,Spring Security 会使用或声明GrantedAuthority中的值以及前缀生成一个列表。scopescpSCOPE_

Keycloak 将领域角色保留在嵌套声明中realm_access.roles。您有两个选项来提取角色并将它们映射到GrantedAuthority.

OAuth2客户端

如果您的应用程序配置为 OAuth2 客户端,则您可以从 ID 令牌或 UserInfo 端点提取角色。Keycloak 仅在访问令牌中包含角色,因此您需要更改配置以将它们也包含在 ID 令牌或 UserInfo 端点中(这就是我在以下示例中使用的)。您可以从 Keycloak 管理控制台执行此操作,转至Client Scopes > roles > Mappers > realm roles

领域角色配置

然后,在 Spring Security 配置中,定义 a GrantedAuthoritiesMapper,它从 UserInfo 端点提取角色并将它们映射到GrantedAuthoritys。在这里,我将介绍特定 bean 的外观。我的 GitHub 上提供了完整的示例: https: //github.com/ThomasVitale/spring-security-examples/tree/main/oauth2/login-user-authorities

@Bean
public GrantedAuthoritiesMapper userAuthoritiesMapperForKeycloak() {
        return authorities -> {
            Set<GrantedAuthority> mappedAuthorities = new HashSet<>();
            var authority = authorities.iterator().next();
            boolean isOidc = authority instanceof OidcUserAuthority;

            if (isOidc) {
                var oidcUserAuthority = (OidcUserAuthority) authority;
                var userInfo = oidcUserAuthority.getUserInfo();

                if (userInfo.hasClaim("realm_access")) {
                    var realmAccess = userInfo.getClaimAsMap("realm_access");
                    var roles = (Collection<String>) realmAccess.get("roles");
                    mappedAuthorities.addAll(generateAuthoritiesFromClaim(roles));
                }
            } else {
                var oauth2UserAuthority = (OAuth2UserAuthority) authority;
                Map<String, Object> userAttributes = oauth2UserAuthority.getAttributes();

                if (userAttributes.containsKey("realm_access")) {
                    var realmAccess =  (Map<String,Object>) userAttributes.get("realm_access");
                    var roles =  (Collection<String>) realmAccess.get("roles");
                    mappedAuthorities.addAll(generateAuthoritiesFromClaim(roles));
                }
            }

            return mappedAuthorities;
        };
    }

Collection<GrantedAuthority> generateAuthoritiesFromClaim(Collection<String> roles) {
        return roles.stream()
                .map(role -> new SimpleGrantedAuthority("ROLE_" + role))
                .collect(Collectors.toList());
}
Run Code Online (Sandbox Code Playgroud)

OAuth2 资源服务器

如果您的应用程序配置为 OAuth2 资源服务器,则您可以从访问令牌中提取角色。在 Spring Security 配置中,定义一个JwtAuthenticationConverterbean,它从访问令牌中提取角色并将它们映射到GrantedAuthoritys。在这里,我将介绍特定 bean 的外观。我的 GitHub 上提供了完整的示例: https: //github.com/ThomasVitale/spring-security-examples/tree/main/oauth2/resource-server-jwt-authorities

public JwtAuthenticationConverter jwtAuthenticationConverterForKeycloak() {
    Converter<Jwt, Collection<GrantedAuthority>> jwtGrantedAuthoritiesConverter = jwt -> {
        Map<String, Collection<String>> realmAccess = jwt.getClaim("realm_access");
        Collection<String> roles = realmAccess.get("roles");
        return roles.stream()
            .map(role -> new SimpleGrantedAuthority("ROLE_" + role))
            .collect(Collectors.toList());
    };

    var jwtAuthenticationConverter = new JwtAuthenticationConverter();
    jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(jwtGrantedAuthoritiesConverter);

    return jwtAuthenticationConverter;
}
Run Code Online (Sandbox Code Playgroud)

  • 你拯救了我的一天,兄弟,谢谢你。 (4认同)