You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot OAuth2登录如何获取Keycloak 22.0.1中的角色

解决Spring Boot OAuth2客户端获取Keycloak 22.0.1角色的问题

可以通过自定义OAuth2用户服务,将Keycloak返回的角色映射为Spring Security的权限,从而实现基于角色的API控制。具体步骤如下:

1. 确认Keycloak角色在Token中的位置

Keycloak默认会把角色放在token的两个位置:

  • realm_access.roles:领域级别的角色
  • resource_access.{你的客户端ID}.roles:当前客户端专属的角色

你可以通过Postman获取的token验证这两个字段是否存在。

2. 自定义OAuth2UserService提取角色

扩展DefaultOAuth2UserService,从返回的用户属性中提取角色并转换为Spring的GrantedAuthority:

import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.user.DefaultOAuth2User;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;

@Service
public class CustomOAuth2UserService extends DefaultOAuth2UserService {

    @Override
    public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
        OAuth2User oAuth2User = super.loadUser(userRequest);
        Map<String, Object> attributes = oAuth2User.getAttributes();

        Collection<SimpleGrantedAuthority> authorities = new ArrayList<>();

        // 提取领域级角色
        Map<String, Object> realmAccess = (Map<String, Object>) attributes.get("realm_access");
        if (realmAccess != null) {
            ((ArrayList<String>) realmAccess.get("roles")).forEach(role ->
                authorities.add(new SimpleGrantedAuthority("ROLE_" + role)));
        }

        // 提取客户端专属角色(按需启用)
        String clientId = userRequest.getClientRegistration().getClientId();
        Map<String, Object> resourceAccess = (Map<String, Object>) attributes.get("resource_access");
        if (resourceAccess != null) {
            Map<String, Object> clientRoles = (Map<String, Object>) resourceAccess.get(clientId);
            if (clientRoles != null) {
                ((ArrayList<String>) clientRoles.get("roles")).forEach(role ->
                    authorities.add(new SimpleGrantedAuthority("ROLE_" + role)));
            }
        }

        // 返回带有权限的OAuth2用户,"sub"是Keycloak用户的唯一标识字段
        return new DefaultOAuth2User(authorities, attributes, "sub");
    }
}

3. 在SecurityFilterChain中配置自定义用户服务

修改你的安全配置,将自定义的OAuth2UserService注入到oauth2登录流程中:

import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserService;
import org.springframework.security.web.SecurityFilterChain;

@EnableWebSecurity
public class SecurityConfig {

    private final OAuth2UserService<OAuth2UserRequest, OAuth2User> customOAuth2UserService;

    public SecurityConfig(OAuth2UserService<OAuth2UserRequest, OAuth2User> customOAuth2UserService) {
        this.customOAuth2UserService = customOAuth2UserService;
    }

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
        httpSecurity
                .authorizeHttpRequests(auth -> auth
                        // 示例:仅允许admin角色访问/admin路径
                        .requestMatchers("/admin/**").hasRole("admin")
                        .anyRequest().authenticated()
                )
                .oauth2Login(oauth2 -> oauth2
                        .userInfoEndpoint(userInfo -> userInfo
                                .userService(customOAuth2UserService)
                        )
                );

        return httpSecurity.build();
    }
}

4. 验证Keycloak客户端配置

确保你的Keycloak客户端已正确配置:

  • 在客户端的Client scopes中添加roles作用域
  • 检查Token mapper中是否存在realm roles和client roles的映射(默认配置会包含,若被删除需重新添加)

完成以上配置后,你就可以通过SecurityContextHolder.getContext().getAuthentication().getAuthorities()获取到用户的角色权限,也能使用@PreAuthorize("hasRole('admin')")或hasRole()方法实现基于角色的权限控制。

内容的提问来源于stack exchange,提问作者IndyStef

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 19:05:09