SpringBoot 3.3配置CORS与RequestMatchers解决403禁止问题
SpringBoot JWT认证React请求403问题排查与解决
1. 自定义CORS配置(最常见触发原因)
默认的cors(Customizer.withDefaults())仅支持简单跨域请求,对于携带Authorization这类自定义头的请求,必须明确配置允许的域名、请求头和方法:
@Configuration public class CorsConfig { @Bean public CorsFilter corsFilter() { UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); CorsConfiguration config = new CorsConfiguration(); // 替换为你的React应用域名,本地开发通常是http://localhost:3000 config.addAllowedOrigin("http://localhost:3000"); // 允许所有请求头,也可指定Authorization等特定头 config.addAllowedHeader("*"); // 允许所有HTTP请求方法 config.addAllowedMethod("*"); // 允许携带Cookie等凭证(按需开启) config.setAllowCredentials(true); source.registerCorsConfiguration("/**", config); return new CorsFilter(source); } }
同时修改WebSecurityConfig中的CORS配置,替换默认实现为自定义配置:
// 在securityFilterChain方法中替换cors部分 .cors(cors -> cors.configurationSource(corsFilter()))
2. 检查React请求的JWT携带格式
确认React请求是否正确添加Authorization头,格式必须为Bearer {token}(注意Bearer后有空格),示例代码:
import axios from 'axios'; const fetchData = async () => { const token = localStorage.getItem('jwtToken'); try { const response = await axios.get('/api/your-endpoint', { headers: { 'Authorization': `Bearer ${token}` } }); console.log(response.data); } catch (error) { console.error(error); } };
若头格式错误,后端JWT过滤器无法解析token,直接返回403。
3. 验证JwtAuthenticationFilter核心逻辑
检查过滤器是否正确提取、验证token并设置认证上下文,关键逻辑示例:
@Component public class JwtAuthenticationFilter extends OncePerRequestFilter { private final JwtService jwtService; private final UserDetailsService userDetailsService; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { final String authHeader = request.getHeader("Authorization"); final String jwt; final String userEmail; // 跳过无Authorization头或格式错误的请求 if (authHeader == null || !authHeader.startsWith("Bearer ")) { filterChain.doFilter(request, response); return; } // 提取Bearer后的token字符串 jwt = authHeader.substring(7); userEmail = jwtService.extractUsername(jwt); // 仅当用户未被认证时执行验证逻辑 if (userEmail != null && SecurityContextHolder.getContext().getAuthentication() == null) { UserDetails userDetails = this.userDetailsService.loadUserByUsername(userEmail); if (jwtService.isTokenValid(jwt, userDetails)) { UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken( userDetails, null, userDetails.getAuthorities() ); authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); SecurityContextHolder.getContext().setAuthentication(authToken); } } filterChain.doFilter(request, response); } }
重点确认token的签名验证、过期时间校验逻辑是否正常。
4. 修正WebSecurityConfig的过滤器注入问题
你的securityFilterChain方法中同时注入了方法参数jwtAuthenticationFilter和类成员jwtAuthFilter,可能导致过滤器实例冲突,建议统一使用类成员注入:
@Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .csrf(AbstractHttpConfigurer::disable) .cors(cors -> cors.configurationSource(corsFilter())) .authorizeHttpRequests(auth -> auth .requestMatchers(HttpMethod.OPTIONS, "/**").permitAll() .requestMatchers("/auth/**").permitAll() .anyRequest().authenticated() ) .sessionManagement(session -> session .sessionCreationPolicy(SessionCreationPolicy.STATELESS) ) .authenticationProvider(authenticationProvider) .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class); return http.build(); }
内容的提问来源于stack exchange,提问作者Juan Felipe Jaramillo Losada
相关产品推荐
相关产品推荐

