Spring Boot 3中JWT身份认证配置失效问题排查与解决
Spring Boot 3.0.2 JWT认证配置问题排查与修复
问题根源分析
你的配置存在三个核心问题:默认密码生成、过滤器错误拦截放行路径、过滤器Bean注册异常,导致预期开放的端点被拦截,且Spring Security fallback到默认用户配置。
修复步骤与代码调整
1. 解决默认密码生成问题
Spring Security生成默认密码,是因为未识别到自定义的UserDetailsService实现类。确保你的UserDetailsServiceImpl标注@Service注解,让Spring容器扫描并注册该Bean:
@Service public class UserDetailsServiceImpl implements UserDetailsService { @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { // 实现从数据库查询用户的逻辑,返回合法的UserDetails实例 // 示例:return User.withUsername(username).password(passwordEncoder.encode("123456")).roles("USER").build(); } }
2. 修正认证过滤器的注册与逻辑
你的AuthenticationTokenFilter错误使用@Configuration注解,导致Bean注册异常;同时过滤器会拦截所有请求(包括放行的端点),无JWT时直接返回401。
修改后的过滤器代码:
@Slf4j @Component // 替换原@Configuration,正确注册为过滤器Bean public class AuthenticationTokenFilter extends OncePerRequestFilter { @Autowired private JwtUtils jwtUtils; @Autowired private UserDetailsServiceImpl userDetailsService; // 定义允许匿名访问的路径集合 private static final List<String> PERMITTED_PATHS = Arrays.asList( "/merchant/auth/**", "/swagger-ui.html", "/swagger-ui/**", "/v3/api-docs/**", "/actuator/**" ); @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { String requestURI = request.getRequestURI(); // 放行允许匿名访问的路径,不执行JWT校验 if (PERMITTED_PATHS.stream().anyMatch(path -> AntPathMatcher.DEFAULT_PATH_MATCHER.match(path, requestURI))) { filterChain.doFilter(request, response); return; } String jwt = ""; String headerAuthorization = request.getHeader("Authorization"); if (StringUtils.hasText(headerAuthorization) && headerAuthorization.startsWith("Bearer ")) { jwt = headerAuthorization.substring(7); } try { if (!jwt.isEmpty() && jwtUtils.validateJwt(jwt)) { String username = jwtUtils.getUsernameFromJwt(jwt); UserDetails userDetails = userDetailsService.loadUserByUsername(username); UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); SecurityContextHolder.getContext().setAuthentication(authentication); filterChain.doFilter(request, response); // 校验通过后放行请求 } else { response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); response.setContentType("application/json"); response.getWriter().write("{\"status\": \"false\", \"message\": \"Access denied\", \"data\": \"Invalid or missing token\"}"); response.getWriter().flush(); } } catch (InvalidKeySpecException | NoSuchAlgorithmException e) { log.error("JWT validation failed:", e); response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); response.setContentType("application/json"); response.getWriter().write("{\"status\": \"false\", \"message\": \"Token validation error\", \"data\": \"Internal server error\"}"); response.getWriter().flush(); } } }
3. 完善SecurityFilterChain配置
补充Swagger完整路径放行、CORS配置,确保跨域请求和Swagger页面正常访问:
@Configuration @EnableMethodSecurity @RequiredArgsConstructor public class ApiSecurityConfig { private final AuthenticationTokenFilter authenticationTokenFilter; private final UserDetailsServiceImpl userDetailsService; private final CustomAuthenticationEntryPoint entryPoint; @Bean public DaoAuthenticationProvider authenticationProvider() { DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider(); authenticationProvider.setUserDetailsService(userDetailsService); authenticationProvider.setPasswordEncoder(passwordEncoder()); return authenticationProvider; } @Bean public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception { return authenticationConfiguration.getAuthenticationManager(); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http.cors(cors -> cors.configurationSource(corsConfigurationSource())) .csrf(csrf -> csrf.disable()) .exceptionHandling(e -> e.authenticationEntryPoint(entryPoint)) .authorizeHttpRequests(authorizeRequests -> authorizeRequests .requestMatchers("/merchant/auth/**").permitAll() .requestMatchers("/swagger-ui.html", "/swagger-ui/**", "/v3/api-docs/**").permitAll() .requestMatchers("/actuator/**").permitAll() .anyRequest().authenticated()) .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)); http.authenticationProvider(authenticationProvider()); http.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class); return http.build(); } // 自定义CORS配置,生产环境请限制allowedOrigins为具体域名 @Bean public CorsConfigurationSource corsConfigurationSource() { CorsConfiguration configuration = new CorsConfiguration(); configuration.setAllowedOrigins(Arrays.asList("*")); configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS")); configuration.setAllowedHeaders(Arrays.asList("Authorization", "Content-Type")); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", configuration); return source; } }
额外检查点
- 确认
JwtUtils中的validateJwt和getUsernameFromJwt方法逻辑正确,确保JWT签名校验、用户名解析功能正常。 - 检查
CustomAuthenticationEntryPoint的commence方法实现,确保未认证时返回JSON格式响应,而非默认页面跳转。
内容的提问来源于stack exchange,提问作者Akin_Glen
相关产品推荐
相关产品推荐

