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
相关产品推荐
相关产品推荐

