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

Spring Boot 3 + JWT认证接口CORS问题修复方案求助

修复Spring Boot 3 + JWT场景下的CORS问题

核心原因

使用Spring Security(JWT认证依赖该框架)时,Security的过滤器链优先级高于普通CORS配置,导致通过WebMvcConfigurer、CorsWebFilter或@CrossOrigin配置的规则被提前拦截,无法生效。必须将CORS规则集成到Security配置中才能解决问题。

可行修复方案

1. 定义全局CORS配置类

创建配置类,设置允许所有来源、请求方法及头信息的规则:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import java.util.List;

@Configuration
public class CorsConfig {
    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration config = new CorsConfiguration();
        // 允许所有来源(Spring Boot 2.4+推荐用AllowedOriginPatterns支持通配符)
        config.setAllowedOriginPatterns(List.of("*"));
        // 允许所有HTTP请求方法
        config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS"));
        // 允许所有请求头(包括JWT的Authorization头)
        config.setAllowedHeaders(List.of("*"));
        // 允许携带凭证(如需前端传递Cookie等)
        config.setAllowCredentials(true);
        // 暴露自定义响应头(比如刷新Token的返回头)
        config.setExposedHeaders(List.of("Authorization"));

        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        // 对所有接口路径应用配置
        source.registerCorsConfiguration("/**", config);
        return source;
    }
}

2. 在Spring Security配置中启用CORS

修改Security配置类,确保CORS过滤器在认证过滤器之前执行,并放行预检OPTIONS请求:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfigurationSource;

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    private final JwtAuthenticationFilter jwtAuthenticationFilter;
    private final CorsConfigurationSource corsConfigurationSource;

    // 构造注入依赖
    public SecurityConfig(JwtAuthenticationFilter jwtAuthenticationFilter, CorsConfigurationSource corsConfigurationSource) {
        this.jwtAuthenticationFilter = jwtAuthenticationFilter;
        this.corsConfigurationSource = corsConfigurationSource;
    }

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
                // 启用CORS,绑定上述全局配置
                .cors(cors -> cors.configurationSource(corsConfigurationSource))
                // JWT场景下禁用CSRF
                .csrf(csrf -> csrf.disable())
                // 无状态会话配置
                .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                // 权限规则配置
                .authorizeHttpRequests(auth -> auth
                        // 放行所有OPTIONS预检请求(浏览器跨域非简单请求必发)
                        .requestMatchers(HttpMethod.OPTIONS, "/**").permitAll()
                        // 放行登录、注册等无需认证的接口
                        .requestMatchers("/api/auth/**").permitAll()
                        // 其余接口需认证
                        .anyRequest().authenticated()
                )
                // 将JWT过滤器添加到Security过滤器链的指定位置
                .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);

        return http.build();
    }
}

3. 清理冲突配置

  • 删除之前通过WebMvcConfigurer实现的跨域配置
  • 移除接口上的@CrossOrigin注解(全局配置已覆盖)
  • 删除独立的CorsWebFilter Bean(避免与Security的CORS配置冲突)

前端请求示例

Axios示例

import axios from 'axios';

// 创建全局Axios实例
const apiClient = axios.create({
  baseURL: 'http://你的后端域名/api',
  timeout: 10000,
});

// 请求拦截器:自动添加JWT Token到请求头
apiClient.interceptors.request.use(config => {
  const token = localStorage.getItem('jwtToken');
  if (token) {
    config.headers.Authorization = `Bearer ${token}`;
  }
  return config;
}, error => Promise.reject(error));

// 调用需要认证的接口示例
async function fetchUserInfo() {
  try {
    const response = await apiClient.get('/user/info');
    console.log('用户信息:', response.data);
  } catch (error) {
    console.error('请求失败:', error.response?.data || error.message);
  }
}

// 登录接口调用示例(无需认证)
async function userLogin(username, password) {
  try {
    const response = await apiClient.post('/auth/login', { username, password });
    localStorage.setItem('jwtToken', response.data.token);
    alert('登录成功');
  } catch (error) {
    console.error('登录失败:', error.response?.data || error.message);
  }
}

Fetch示例

// 带JWT的GET请求示例
async function fetchUserInfo() {
  const token = localStorage.getItem('jwtToken');
  try {
    const response = await fetch('http://你的后端域名/api/user/info', {
      method: 'GET',
      headers: {
        'Authorization': `Bearer ${token}`,
        'Content-Type': 'application/json'
      }
    });
    if (!response.ok) throw new Error(`HTTP错误:${response.status}`);
    const data = await response.json();
    console.log('用户信息:', data);
  } catch (error) {
    console.error('请求失败:', error.message);
  }
}

// 登录请求示例
async function userLogin(username, password) {
  try {
    const response = await fetch('http://你的后端域名/api/auth/login', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ username, password })
    });
    if (!response.ok) throw new Error(`HTTP错误:${response.status}`);
    const data = await response.json();
    localStorage.setItem('jwtToken', data.token);
    alert('登录成功');
  } catch (error) {
    console.error('登录失败:', error.message);
  }
}

内容的提问来源于stack exchange,提问作者Achraf Ait Ibba

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 13:12:53