Spring OAuth2 OIDC:如何获取用户信息与AD组信息用于授权
解决方案
1. 创建Spring Security配置类
由于需要自定义授权逻辑和用户信息获取,必须创建SecurityConfig类替代默认自动配置:
import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter; import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter; import org.springframework.security.web.SecurityFilterChain; import org.springframework.web.client.RestTemplate; @Configuration @EnableWebSecurity public class SecurityConfig { @Value("${spring.security.oauth2.resourceserver.user-info-uri}") private String userInfoEndpoint; @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(auth -> auth // 基础规则:所有请求需认证 .anyRequest().authenticated() ) .oauth2ResourceServer(oauth2 -> oauth2 .jwt(jwt -> jwt .jwtAuthenticationConverter(jwtAuthenticationConverter(restTemplate())) ) ); return http.build(); } @Bean public RestTemplate restTemplate() { return new RestTemplate(); } // 自定义JWT转换器,整合用户信息端点数据 private JwtAuthenticationConverter jwtAuthenticationConverter(RestTemplate restTemplate) { CustomJwtAuthenticationConverter converter = new CustomJwtAuthenticationConverter(restTemplate, userInfoEndpoint); // 配置JWT内置角色解析规则(按需调整) JwtGrantedAuthoritiesConverter grantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter(); grantedAuthoritiesConverter.setAuthorityPrefix("ROLE_"); grantedAuthoritiesConverter.setAuthoritiesClaimName("groups"); converter.setJwtGrantedAuthoritiesConverter(grantedAuthoritiesConverter); return converter; } }
同时在application.properties中添加用户信息端点配置:
spring.security.oauth2.resourceserver.user-info-uri=https://<org-auth-url>/userinfo
2. 自定义JWT认证转换器,获取用户与AD组信息
创建自定义转换器,在JWT验证通过后主动调用用户信息端点,将返回的用户详情和AD组存入认证对象:
import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.security.core.Authentication; import org.springframework.security.oauth2.jwt.Jwt; import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter; import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken; import org.springframework.web.client.RestTemplate; import org.springframework.core.ParameterizedTypeReference; import java.util.List; import java.util.Map; public class CustomJwtAuthenticationConverter extends JwtAuthenticationConverter { private final RestTemplate restTemplate; private final String userInfoEndpoint; public CustomJwtAuthenticationConverter(RestTemplate restTemplate, String userInfoEndpoint) { this.restTemplate = restTemplate; this.userInfoEndpoint = userInfoEndpoint; } @Override protected Authentication convert(Jwt jwt) { Authentication authentication = super.convert(jwt); String token = jwt.getTokenValue(); // 构造请求头,携带Bearer Token调用用户信息端点 HttpHeaders headers = new HttpHeaders(); headers.setBearerAuth(token); HttpEntity<Void> requestEntity = new HttpEntity<>(headers); ResponseEntity<Map<String, Object>> responseEntity = restTemplate.exchange( userInfoEndpoint, HttpMethod.GET, requestEntity, new ParameterizedTypeReference<>() {} ); Map<String, Object> userInfo = responseEntity.getBody(); // 将用户信息和AD组封装到自定义UserDetails中 if (authentication instanceof JwtAuthenticationToken tokenAuth) { CustomUserDetails userDetails = new CustomUserDetails( tokenAuth.getName(), (String) userInfo.get("username"), (List<String>) userInfo.get("adGroups") ); return new JwtAuthenticationToken( jwt, tokenAuth.getAuthorities(), tokenAuth.getName(), userDetails ); } return authentication; } }
3. 自定义UserDetails存储用户与AD组数据
创建CustomUserDetails类,统一封装用户基础信息和AD组,同时实现Spring Security的UserDetails接口用于授权:
import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import java.util.Collection; import java.util.List; import java.util.stream.Collectors; public class CustomUserDetails implements UserDetails { private final String userId; private final String username; private final List<String> adGroups; public CustomUserDetails(String userId, String username, List<String> adGroups) { this.userId = userId; this.username = username; this.adGroups = adGroups; } // 将AD组转换为Spring Security可识别的权限对象 @Override public Collection<? extends GrantedAuthority> getAuthorities() { return adGroups.stream() .map(group -> (GrantedAuthority) () -> "GROUP_" + group) .collect(Collectors.toList()); } @Override public String getPassword() { return null; // JWT认证无需密码 } @Override public String getUsername() { return username; } @Override public boolean isAccountNonExpired() { return true; } @Override public boolean isAccountNonLocked() { return true; } @Override public boolean isCredentialsNonExpired() { return true; } @Override public boolean isEnabled() { return true; } // 自定义方法,用于获取AD组列表 public List<String> getAdGroups() { return adGroups; } public String getUserId() { return userId; } }
4. 配置基于AD组的授权规则
修改SecurityConfig中的securityFilterChain方法,添加基于AD组的路径授权规则:
http .authorizeHttpRequests(auth -> auth // 允许AD组"ADMIN_GROUP"访问/admin/**路径 .antMatchers("/admin/**").hasAuthority("GROUP_ADMIN_GROUP") // 允许AD组"USER_GROUP"访问/user/**路径 .antMatchers("/user/**").hasAuthority("GROUP_USER_GROUP") .anyRequest().authenticated() )
5. 在控制器中获取用户详情
通过@AuthenticationPrincipal注解直接获取当前认证用户的自定义信息:
import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class UserController { @GetMapping("/user/me") public CustomUserDetails getCurrentUser(@AuthenticationPrincipal JwtAuthenticationToken authentication) { return (CustomUserDetails) authentication.getDetails(); } }
注意事项
- 若用户信息端点返回的字段结构与示例不同,需调整
CustomJwtAuthenticationConverter中解析userInfo的逻辑 - 可添加Spring Cache缓存用户信息(按Token或用户ID缓存),避免重复调用用户信息端点
- 确保用户信息端点支持Bearer Token认证,请求头格式为
Authorization: Bearer {token}
内容的提问来源于stack exchange,提问作者user2094311
相关产品推荐
相关产品推荐

