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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 16:29:55