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

Spring Security 6+Angular 13 OPTIONS请求CORS问题求助

解决方案

核心问题是OPTIONS预检请求未携带JWT,被JwtAuthenticationFilter拦截后无法返回CORS响应头,以下是几种在保证/operator/**接口认证要求的前提下,可落地的修复方案:

方案一:Spring Security中放行OPTIONS请求+全局CORS配置

在SecurityFilterChain中先配置CORS规则,同时明确放行所有OPTIONS请求,让预检请求无需认证即可拿到合法响应头。

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    http
        // 优先配置CORS
        .cors(cors -> cors.configurationSource(corsConfigurationSource()))
        // 前后端分离场景禁用CSRF
        .csrf(csrf -> csrf.disable())
        // 权限规则配置
        .authorizeHttpRequests(auth -> auth
            // 放行所有OPTIONS预检请求
            .requestMatchers(HttpMethod.OPTIONS, "/**").permitAll()
            // 放行登录接口
            .requestMatchers("/auth/login").permitAll()
            // /operator/**接口仅允许ADMIN角色访问
            .requestMatchers("/operator/**").hasRole("ADMIN")
            // 其余接口需认证
            .anyRequest().authenticated()
        )
        // 添加JWT认证过滤器
        .addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
    
    return http.build();
}

// 配置全局CORS规则
@Bean
public CorsConfigurationSource corsConfigurationSource() {
    CorsConfiguration config = new CorsConfiguration();
    // 允许前端域名(生产环境替换为实际域名)
    config.setAllowedOrigins(List.of("http://localhost:4200"));
    // 允许的HTTP方法,必须包含OPTIONS
    config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS"));
    // 允许所有请求头(包括Authorization)
    config.setAllowedHeaders(List.of("*"));
    // 允许携带凭证(如JWT存于Cookie时需开启)
    config.setAllowCredentials(true);
    // 预检请求缓存时长,减少重复OPTIONS请求
    config.setMaxAge(3600L);
    
    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    source.registerCorsConfiguration("/**", config);
    return source;
}

方案二:让CORS过滤器优先于JWT过滤器执行

通过FilterRegistrationBean设置CORS过滤器的最高优先级,确保在JWT认证拦截前,OPTIONS请求就能拿到CORS响应头。

@Bean
public FilterRegistrationBean<CorsFilter> corsFilter() {
    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    CorsConfiguration config = new CorsConfiguration();
    config.setAllowedOrigins(List.of("http://localhost:4200"));
    config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS"));
    config.setAllowedHeaders(List.of("*"));
    config.setAllowCredentials(true);
    config.setMaxAge(3600L);
    source.registerCorsConfiguration("/**", config);
    
    FilterRegistrationBean<CorsFilter> bean = new FilterRegistrationBean<>(new CorsFilter(source));
    // 设置最高优先级,确保在JWT过滤器前执行
    bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
    return bean;
}

同时在Security配置中保持OPTIONS请求的放行规则(同方案一的authorizeHttpRequests配置)。

方案三:在JWT过滤器中直接放行OPTIONS请求

修改自定义的JwtAuthenticationFilter,在认证逻辑前判断请求方法,若为OPTIONS则直接设置CORS头并放行,不执行JWT校验。

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
    // 拦截OPTIONS预检请求,直接返回CORS响应头
    if (HttpMethod.OPTIONS.name().equalsIgnoreCase(request.getMethod())) {
        response.setHeader("Access-Control-Allow-Origin", "http://localhost:4200");
        response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
        response.setHeader("Access-Control-Allow-Headers", "*");
        response.setHeader("Access-Control-Allow-Credentials", "true");
        response.setHeader("Access-Control-Max-Age", "3600");
        response.setStatus(HttpServletResponse.SC_OK);
        return;
    }
    
    // 原有JWT认证逻辑
    String jwt = extractJwtFromRequest(request);
    // ...后续校验、设置认证信息等代码
    filterChain.doFilter(request, response);
}

注意事项

  • 推荐优先使用方案一或方案二,这是Spring生态下的标准配置方式,更易维护;方案三适合快速临时修复。
  • 生产环境中allowedOrigins不要设置为*,需指定具体的前端域名,否则无法携带凭证。

内容的提问来源于stack exchange,提问作者Matteo Vigilante

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 18:05:21