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

Docker部署Spring Boot应用出现授权头被拒、CORS跨域拦截问题

问题根因

两个配置错误分别导致CORS拦截、授权头被拒的问题:

  • Nginx反向代理配置错误
    你在/api代理块中强制配置proxy_set_header Host localhost;,覆盖了原始请求的Host头信息。Spring内置CORS处理器会校验请求Host与Origin的匹配逻辑,即使https://app.myapp.fr已经加入允许Origin列表,Host被篡改为localhost后会直接判定Origin不合法,抛出你看到的拒绝日志。
    此外配置没有透传Authorization认证头,是授权请求被拒绝的直接原因;也没有单独处理浏览器CORS预检的OPTIONS请求,这类请求默认不带认证信息,会先被Spring Security规则拦截返回401,导致CORS校验流程根本无法正常走完。
  • Spring CORS配置不符合带认证请求的规范
    你将allowedMethods、allowedHeaders设置为通配符*,但Spring CORS规范明确要求:对于携带Authorization认证头的请求,通配符*不会生效,必须显式声明允许的方法和请求头;同时你没有将OPTIONS预检请求加入免认证路径,也没有显式开启认证头携带权限,会进一步加剧拦截问题。
修复方案

按顺序修改Nginx和Spring Security配置即可。

1. 修正Nginx配置

替换原有/api路径的location块,删除错误的Host头配置,补全请求透传逻辑,单独放行OPTIONS预检请求:

location /api {
    # 直接响应OPTIONS预检请求,减少后端转发开销
    if ($request_method = 'OPTIONS') {
        add_header 'Access-Control-Allow-Origin' 'https://app.myapp.fr';
        add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS';
        add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type';
        add_header 'Access-Control-Max-Age' 3600;
        return 204;
    }
    proxy_pass   http://app-backend:8087/api;
    # 透传原始请求信息,禁止强制改写Host为localhost
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    # 显式透传Authorization认证头,避免认证信息丢失
    proxy_set_header Authorization $http_authorization;
}

修改完成后执行nginx -s reload重载配置生效。

2. 修正Spring Security配置

调整CORS配置逻辑,显式声明允许的请求方法、头信息,将OPTIONS请求加入免认证白名单,确保过滤器顺序正确:

@Override
protected void configure(final HttpSecurity httpSecurity) throws Exception {
    CorsConfiguration configuration = new CorsConfiguration();
    configuration.setAllowedOrigins(Arrays.asList("https://app.myapp.fr", "http://localhost:3000"));
    // 显式声明允许的请求方法,禁止使用通配符*
    configuration.setAllowedMethods(Arrays.asList("GET","POST","PUT","DELETE","OPTIONS"));
    // 显式声明允许的请求头,包含Authorization认证头
    configuration.setAllowedHeaders(Arrays.asList("Authorization", "Content-Type", "X-Requested-With"));
    // 允许携带认证凭证
    configuration.setAllowCredentials(true);
    configuration.setMaxAge(3600L);

    UrlBasedCorsConfigurationSource corsSource = new UrlBasedCorsConfigurationSource();
    corsSource.registerCorsConfiguration("/**", configuration);

    httpSecurity.cors().configurationSource(corsSource)
            .and()
            .csrf().disable()
            .exceptionHandling().authenticationEntryPoint(this.jwtAuthenticationEntryPoint).and()
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
            .authorizeRequests()
            // 所有OPTIONS预检请求全部放行,不需要认证
            .antMatchers(OPTIONS, "/**").permitAll()
            .antMatchers(POST, "/connexion").permitAll()
            .antMatchers(POST, "/inscription").permitAll()
            .antMatchers(POST, "/activation").permitAll()
            .anyRequest()
            .authenticated()
            .and().httpBasic();

    final JwtAuthorizationTokenFilter authenticationTokenFilter = new JwtAuthorizationTokenFilter(this.profileService, this.jwtTokenUtil, this.tokenHeader);
    // 确保JWT过滤器在CORS校验逻辑之后执行
    httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
    httpSecurity
            .headers()
            .frameOptions().sameOrigin()
            .cacheControl();
}

注意需要提前导入OPTIONS方法的静态常量:import static org.springframework.http.HttpMethod.OPTIONS;,和你已有的POST常量导入方式一致。

配置修改完成后重启后端服务,清空浏览器缓存重试即可。

优化提示:由于你是前后端同域名部署,前端API请求可以改用相对路径(比如直接写/api/connexion而非硬编码全路径https://app.myapp.fr/api/connexion),这种场景下浏览器不会触发CORS校验,后续维护成本更低。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 02:18:20