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
相关产品推荐
相关产品推荐

