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

如何在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资源服务器模块,迁移后你原本的控制器代码可以直接生效,无需手动转换认证对象。

  1. 先替换依赖(Spring Boot环境):移除旧的spring-security-oauth2依赖,引入官方starter:
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
  1. 替换配置类:
@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();
    }
}
  1. 你原本的控制器代码可以直接运行,@AuthenticationPrincipal Jwt principal会自动注入不为空,直接调用getClaimAsString("userId")等方法即可提取对应声明。

常见踩坑点

  • 请求头Authorization的格式必须为Bearer {JWT字符串},Bearer和JWT之间必须有空格
  • 确保你持有的公钥和签发JWT的私钥是配对的,且JWT未被篡改、未过期
  • 自定义声明要确保存在于JWT的payload段,没有被加密处理

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.04 05:42:03