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

Spring Boot中JWT令牌携带用户ID实现及解析错误排查

你的JWT相关问题解决方案

嗨,我来帮你拆解并解决这三个Spring Boot + JWT场景下的常见问题:

问题1:纯JWT(无OAuth2)携带用户ID的实现方式

不用OAuth2完全可以实现JWT携带用户ID,核心是在生成JWT时把用户ID作为自定义Claim加入令牌,解析时再从中取出。具体步骤如下:

  • 第一步:自定义UserDetails实现类,包含用户ID字段
    创建CustomUserDetails类,实现UserDetails接口并添加userId属性:

    public class CustomUserDetails implements UserDetails {
        private Long userId;
        private String username;
        private String password;
        private Collection<? extends GrantedAuthority> authorities;
    
        // 构造方法、getter/setter,以及UserDetails接口要求的isAccountNonExpired等方法实现
    }
    
  • 第二步:编写JWT生成工具类,加入用户ID到令牌
    写一个JwtTokenProvider组件,负责JWT的生成与解析:

    @Component
    public class JwtTokenProvider {
        @Value("${jwt.secret}")
        private String secretKey;
        @Value("${jwt.expiration}")
        private long validityInMilliseconds;
    
        // 生成带用户ID的JWT令牌
        public String createToken(CustomUserDetails userDetails) {
            Claims claims = Jwts.claims().setSubject(userDetails.getUsername());
            claims.put("userId", userDetails.getUserId()); // 把用户ID加入自定义Claim
            claims.put("roles", userDetails.getAuthorities().stream()
                    .map(GrantedAuthority::getAuthority)
                    .collect(Collectors.toList()));
    
            Date now = new Date();
            Date validity = new Date(now.getTime() + validityInMilliseconds);
    
            return Jwts.builder()
                    .setClaims(claims)
                    .setIssuedAt(now)
                    .setExpiration(validity)
                    .signWith(SignatureAlgorithm.HS256, secretKey)
                    .compact();
        }
    
        // 从JWT中解析用户ID
        public Long getUserIdFromToken(String token) {
            Claims claims = Jwts.parser()
                    .setSigningKey(secretKey)
                    .parseClaimsJws(token)
                    .getBody();
            return claims.get("userId", Long.class);
        }
    
        // 辅助方法:验证token有效性、获取用户名等
    }
    
  • 第三步:登录接口中生成并返回带用户ID的令牌
    在登录成功后,调用工具类生成令牌并返回给前端:

    @PostMapping("/login")
    public ResponseEntity<?> authenticateUser(@RequestBody LoginRequest loginRequest) {
        // 用户名密码验证逻辑
        CustomUserDetails userDetails = customUserDetailsService.loadUserByUsername(loginRequest.getUsername());
        String jwt = jwtTokenProvider.createToken(userDetails);
        return ResponseEntity.ok(new JwtResponse(jwt, userDetails.getUserId(), userDetails.getUsername()));
    }
    

问题2:继承WebSecurityConfigurerAdapter时扩展JWT(类似TokenEnhancer)

OAuth2的TokenEnhancer是用来扩展OAuth2令牌的,但纯JWT场景下不需要依赖它——我们可以通过自定义JWT生成逻辑实现同样的扩展效果,本质就是在生成JWT时添加自定义Claims,就像上面JwtTokenProvider里做的那样。

在你的WebSecurityConfigurerAdapter配置类中,只需注入自定义的JwtTokenProvider,并在过滤器中使用它解析令牌即可:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private JwtTokenProvider jwtTokenProvider;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .authorizeRequests()
                .antMatchers("/login").permitAll()
                .anyRequest().authenticated()
                .and()
                .addFilterBefore(new JWTAuthenticationFilter(jwtTokenProvider), UsernamePasswordAuthenticationFilter.class);
    }

    // 其他配置:密码编码器、UserDetailsService实例化等
}

这种方式比OAuth2的TokenEnhancer更轻量,完全适配纯JWT场景。

问题3:JsonParseException错误的解决

你的错误是因为直接把整个JWT字符串传给objectMapper.readTree(jwt)——JWT是由.分隔的三段式字符串(Header.Payload.Signature),并不是纯JSON,所以直接解析会抛出异常。

正确的做法是先提取中间的Payload部分,Base64解码后再解析成JSON:

// 在JWTAuthenticationFilter中添加Payload提取方法
private String extractJwtPayload(String jwt) {
    String[] jwtParts = jwt.split("\\.");
    if (jwtParts.length != 3) {
        throw new IllegalArgumentException("无效的JWT令牌格式");
    }
    // JWT使用URL安全的Base64编码,需要用对应解码器
    return new String(Base64.getUrlDecoder().decode(jwtParts[1]));
}

// 修复解析逻辑
String payload = extractJwtPayload(jwt);
JsonNode jsonJwt = objectMapper.readTree(payload);
Long userId = jsonJwt.get("userId").asLong();

这样就能正确解析出JWT中的用户ID等自定义信息了。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 06:58:58