如何在Spring Service或Controller中获取JWT claims及排查principal为null问题
核心原因
你当前使用的是已废弃的Spring Security OAuth 2.x资源服务器模块(带@EnableResourceServer、ResourceServerConfigurerAdapter的旧方案),该模块默认的认证主体类型不是Spring Security 5.2+内置的org.springframework.security.oauth2.jwt.Jwt,因此用@AuthenticationPrincipal Jwt注入会拿到空值。
两种解决方案
方案1:适配当前旧版配置(无需改依赖)
直接从认证对象中提取JWT声明即可,无需修改现有依赖和配置类,控制器代码示例:
@GetMapping("/user/info") public Map<String, Object> getUserInfo(Authentication authentication) { OAuth2Authentication oauth2Authentication = (OAuth2Authentication) authentication; // 提取JWT的所有payload声明 Map<String, Object> jwtClaims = (Map<String, Object>) oauth2Authentication.getUserAuthentication().getDetails(); Map<String, String> result = new HashMap<>(); result.put("user_name", (String) jwtClaims.get("preferred_username")); result.put("user_id", (String) jwtClaims.get("userId")); result.put("organization", (String) jwtClaims.get("organization")); return Collections.unmodifiableMap(result); }
如果以上代码拿不到claims,请检查你的ResourceServerConfig是否配置了关联公钥的JwtTokenStore和DefaultTokenServices,示例配置补充:
@Configuration @EnableResourceServer public class ResourceServerConfig extends ResourceServerConfigurerAdapter { @Value("${jwt.public-key}") private RSAPublicKey publicKey; @Override public void configure(HttpSecurity http) throws Exception { http.csrf().disable() .authorizeRequests() .antMatchers("/**").authenticated(); } @Override public void configure(ResourceServerSecurityConfigurer resources) throws Exception { resources.tokenServices(tokenServices()); } @Bean public TokenStore tokenStore() { return new JwtTokenStore(jwtAccessTokenConverter()); } @Bean public JwtAccessTokenConverter jwtAccessTokenConverter() { JwtAccessTokenConverter converter = new JwtAccessTokenConverter(); converter.setPublicKey(publicKey); return converter; } @Bean public DefaultTokenServices tokenServices() { DefaultTokenServices services = new DefaultTokenServices(); services.setTokenStore(tokenStore()); return services; } }
方案2:迁移到官方推荐的原生JWT资源服务器(推荐)
Spring Security 5.2之后官方已原生支持JWT资源服务器,废弃了旧的OAuth2资源服务器模块,迁移后你原本的控制器代码可以直接生效,无需手动转换认证对象。
- 先替换依赖(Spring Boot环境):移除旧的
spring-security-oauth2依赖,引入官方starter:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-oauth2-resource-server</artifactId> </dependency>
- 替换配置类:
@Configuration @EnableWebSecurity public class JwtSecurityConfig { @Value("${jwt.public-key}") private RSAPublicKey publicKey; @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { return http .csrf(csrf -> csrf.disable()) .authorizeHttpRequests(auth -> auth.anyRequest().authenticated()) .oauth2ResourceServer(oauth2 -> oauth2.jwt(jwt -> jwt.decoder(jwtDecoder()))) .build(); } @Bean public JwtDecoder jwtDecoder() { // 用你持有的公钥构建JWT解码器,自动完成验签 return NimbusJwtDecoder.withPublicKey(publicKey).build(); } }
- 你原本的控制器代码可以直接运行,
@AuthenticationPrincipal Jwt principal会自动注入不为空,直接调用getClaimAsString("userId")等方法即可提取对应声明。
常见踩坑点
- 请求头
Authorization的格式必须为Bearer {JWT字符串},Bearer和JWT之间必须有空格 - 确保你持有的公钥和签发JWT的私钥是配对的,且JWT未被篡改、未过期
- 自定义声明要确保存在于JWT的payload段,没有被加密处理
内容的提问来源于stack exchange,提问作者BoomShaka
相关产品推荐
相关产品推荐

