Spring Boot集成JWT认证登录失败,报NoClassDefFoundError错误
问题现象
在Spring Boot中实现JWT身份认证时,登录功能失败,抛出以下错误日志:
2024-02-29T12:22:12.726+05:30 ERROR 3552 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Handler dispatch failed: java.lang.NoClassDefFoundError: javax/xml/bind/DatatypeConverter] with root cause
2024-02-29T12:22:12.735+05:30 DEBUG 3552 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy : Securing POST /error2024-02-29T12:22:12.735+05:30 DEBUG 3552 --- [nio-8080-exec-1] o.s.s.w.a.AnonymousAuthenticationFilter : Set SecurityContextHolder to anonymous SecurityContext
核心代码
JWT认证过滤器
@Component public class JwtAuthenticationFilter extends OncePerRequestFilter { @Autowired private UserDetailsService userDetailsService; @Autowired private JwtTokenHelper jwtTokenHelper; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { String requestToken = request.getHeader("Authorization"); String username = null; String token = null; if (requestToken != null && requestToken.startsWith("Bearer")) { token = requestToken.substring(7); try{username=this.jwtTokenHelper.getUsernameFromToken(token);} catch (IllegalArgumentException e){System.out.println("Unable to get jwt token");} catch(ExpiredJwtException e){System.out.println("Jwt token has expired");} catch (MalformedJwtException e){System.out.println("Invalid jwt");} } else{System.out.println(" Jwt token does not begin with Bearer");} if (username != null && SecurityContextHolder.getContext().getAuthentication()==null){ UserDetails userDetails =this.userDetailsService.loadUserByUsername(username); if(this.jwtTokenHelper.validateToken(token,userDetails)){ UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(userDetails,null,userDetails.getAuthorities()); usernamePasswordAuthenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken); } else{ System.out.println("Invalid jwt");} } else{ } filterChain.doFilter(request,response); }}
认证控制器
@RestController @RequestMapping("api/v1/auth") public class AuthController { @Autowired private JwtTokenHelper jwtTokenHelper; @Autowired private UserDetailsService userDetailsService; @Autowired public AuthenticationManager authenticationManager; @Autowired private UserService userService; @PostMapping("/login") public ResponseEntity<JwtAuthResponse> createToken(@RequestBody JwtAuthRequest request) throws Exception{ this.authenticate(request.getUsername(),request.getPassword()); UserDetails userDetails = this.userDetailsService.loadUserByUsername(request.getUsername()); String token = this.jwtTokenHelper.generateToken(userDetails); JwtAuthResponse response = new JwtAuthResponse(); response.setToken(token); return new ResponseEntity<JwtAuthResponse>(response, HttpStatus.OK); } private void authenticate(String username, String password) throws Exception{ UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username,password); try{ authenticationManager.authenticate(authenticationToken); }catch (BadCredentialsException e){ System.out.println("Invalid Details"); throw new ApiException("Invalid Username or Password"); } } @PostMapping("/register") public ResponseEntity<UserDto> regidterUser(@RequestBody UserDto userDto){ UserDto registerdUser = this.userService.registerNewUser(userDto); return new ResponseEntity<UserDto>(registerdUser,HttpStatus.CREATED); }}
配置类
@Configuration class MyConfig { @Autowired UserRepo userRepo; @Bean public DaoAuthenticationProvider authProvider() { DaoAuthenticationProvider provider = new DaoAuthenticationProvider(); provider.setPasswordEncoder(passwordEncoder()); provider.setUserDetailsService(userDetailsService()); return provider; } @Bean public UserDetailsService userDetailsService() { return new UserDetailsService() { @Override public UserDetails loadUserByUsername(String username) { return userRepo.findByEmail(username).orElseThrow(()-> new ResourceNotFoundException("User","email:"+username,0)); } }; } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Bean public AuthenticationManager authenticationManager(AuthenticationConfiguration builder) throws Exception { return builder.getAuthenticationManager(); }}
安全配置类
@Configuration @EnableWebSecurity @EnableWebMvc public class SecurityConfig{ public static final String[] PUBLIC_URLS={ "api/v1/auth/**", "/v3/api-docs", "/v2/api-docs", "/swagger-resources/**", "/swagger-ui/**", "/webjars/**" }; @Autowired private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint; @Autowired private JwtAuthenticationFilter jwtAuthenticationFilter; @Bean protected SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .csrf(AbstractHttpConfigurer::disable) .authorizeHttpRequests((authz)-> authz .requestMatchers(PUBLIC_URLS).permitAll() .requestMatchers(HttpMethod.GET).permitAll() .anyRequest() .authenticated()) .exceptionHandling(ex -> ex.authenticationEntryPoint(this.jwtAuthenticationEntryPoint)) .sessionManagement(session->session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)); http .addFilterBefore( this.jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); return http.build(); }}
错误原因分析
javax.xml.bind.DatatypeConverter是JDK 8及以前的标准库类,但从JDK 9开始,JAXB模块被移除出默认类路径(转为可选模块)。如果你的Spring Boot项目使用JDK 9+,且依赖的JWT库(如旧版本jjwt)仍依赖该类,就会触发NoClassDefFoundError。
常见场景是使用了io.jsonwebtoken:jjwt:0.9.1这类旧版JJWT,它们依赖JAXB的DatatypeConverter处理Base64编解码。
解决方案
方案1:升级JJWT到最新稳定版
新版本JJWT已移除对JAXB的依赖,改用JDK自带Base64工具类或内置实现,推荐升级到0.11.5及以上版本:
<dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-api</artifactId> <version>0.11.5</version> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-impl</artifactId> <version>0.11.5</version> <scope>runtime</scope> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-jackson</artifactId> <version>0.11.5</version> <scope>runtime</scope> </dependency>
注意:升级后需检查JwtTokenHelper中的代码是否适配新版本API(如JWT创建、解析方法的变化)。
方案2:添加JAXB依赖(仅兼容旧版JJWT,不推荐长期使用)
若无法升级JJWT,可手动添加JAXB依赖让JDK 9+找到缺失类:
<dependency> <groupId>javax.xml.bind</groupId> <artifactId>jaxb-api</artifactId> <version>2.3.1</version> </dependency> <dependency> <groupId>com.sun.xml.bind</groupId> <artifactId>jaxb-core</artifactId> <version>2.3.0.1</version> </dependency> <dependency> <groupId>com.sun.xml.bind</groupId> <artifactId>jaxb-impl</artifactId> <version>2.3.3</version> </dependency>
内容的提问来源于stack exchange,提问作者Jaydeepsinh Parmar

