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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 20:01:33