Spring Boot+AWS环境下CORS跨域问题排查求助
问题:CORS错误排查建议
部署架构与问题现象
- ReactJS应用部署在AWS Amplify,访问地址为
https://example.com - Spring Boot应用部署在EC2实例,通过NGINX以
api.example.com对外提供服务 - ReactJS通过
https://api.example.com/...与后端通信 - 禁用Chrome安全设置(含CORS)时可正常获取数据,但正常Chrome/Firefox环境下出现CORS错误
已配置内容
Spring Boot CORS配置
@Bean public CorsFilter corsFilter() throws Exception{ CorsConfiguration config = new CorsConfiguration(); config.setAllowedOriginPatterns(List.of("*")); // Allow all origins config.addAllowedHeader("*"); config.addAllowedMethod("*"); config.setAllowCredentials(true); config.addExposedHeader("code"); config.addExposedHeader("reason"); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", config); log.info(new ObjectMapper().writeValueAsString(config)); return new CorsFilter(source); }
NGINX配置(/etc/nginx/sites-available/api.example.com)
upstream backend-service-api.example.com { server localhost:8080; #server search - service - 3: 8082; } server { server_name api.example.com; location /{ proxy_pass https://example.com; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; proxy_set_header Host $host; proxy_cache_bypass $http_upgrade; } location ~* "^/api/backapp-service(/|$)(.*)" { rewrite "(?i)/api/backapp-service(/|$)(.*)" /$2 break; # Handle preflight requests if ($request_method = 'OPTIONS') { add_header 'Access-Control-Allow-Origin' 'https://example.com'; add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; add_header 'Access-Control-Allow-Headers' 'Origin, Content-Type, Accept, Authorization'; add_header 'Access-Control-Allow-Credentials' 'true'; add_header 'Content-Length' 0; add_header 'Content-Type' 'text/plain'; return 204; return 204; } # Handle actual requests add_header 'Access-Control-Allow-Origin' 'https://example.com'; add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; add_header 'Access-Control-Allow-Headers' 'Origin, Content-Type, Accept, Authorization'; add_header 'Access-Control-Allow-Credentials' 'true'; proxy_pass http://backend-service-api.example.com; } location ~* "^/media(/|$)(.*)" { rewrite "(?i)/media(/|$)(.*)" /$2 break; root /home/ubuntu/PRODUCTION/FileServer; } error_page 401 = @error401; location @error401 { add_header 'Access-Control-Allow-Origin' 'https://example.com'; add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; add_header 'Access-Control-Allow-Headers' 'Origin, Content-Type, Accept, Authorization'; add_header 'Access-Control-Allow-Credentials' 'true'; return 401; } listen [::]:443 ssl ipv6only=on; # managed by Certbot listen 443 ssl; # managed by Certbot ssl_certificate /etc/letsencrypt/live/api.example.com-0001/fullchain.pem; # managed by Certbot ssl_certificate_key /etc/letsencrypt/live/api.example.com-0001/privkey.pem; # managed by Certbot include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot } server { if ($host = api.example.com) { return 301 https://$host$request_uri; } # managed by Certbot listen 80; listen [::]:80; server_name api.example.com; return 404; # managed by Certbot }
OPTIONS请求响应(curl -I -X OPTIONS https://api.example.com/api/backapp-service/products/v1/search-product)
HTTP/1.1 200 Server: nginx/1.24.0 (Ubuntu) Date: Wed, 30 Oct 2024 15:19:09 GMT Content-Length: 0 Connection: keep-alive reason: Invalid JWT token::CharSequence cannot be null or empty. code: 401 Vary: Origin Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers Allow: GET,HEAD,POST,OPTIONS Accept-Patch: X-Content-Type-Options: nosniff X-XSS-Protection: 0 Cache-Control: no-cache, no-store, max-age=0, must-revalidate Pragma: no-cache Expires: 0 Access-Control-Allow-Origin: https://example.com Access-Control-Allow-Methods: GET, POST, OPTIONS Access-Control-Allow-Headers: Origin, Content-Type, Accept, Authorization Access-Control-Allow-Credentials: true
排查建议
- 注意到OPTIONS预检请求返回了
code:401错误,说明Spring Boot的鉴权逻辑拦截了该请求。预检请求默认不会携带Token,需在Spring Security(如果使用)中配置放行OPTIONS请求,或让鉴权过滤器跳过OPTIONS方法。 - 同时在Spring Boot和Nginx配置CORS可能导致冲突,建议统一在一处配置:要么保留Spring Boot的CORS配置并禁用Nginx的相关配置,要么只保留Nginx的CORS配置并注释掉Spring Boot的
CorsFilterBean,避免重复添加响应头引发浏览器报错。 - 检查Nginx的location匹配优先级:第一个
location /会匹配所有请求,后续正则匹配的location ~* "^/api/backapp-service(/|$)(.*)"优先级更高,但需确认请求是否真的命中该location。可在Nginx的access_log中添加$request_uri和$location字段验证。 - 查看浏览器控制台的具体CORS错误信息,明确是
Access-Control-Allow-Origin不匹配、缺少响应头还是凭证相关问题,针对性排查。 - Spring Boot中
setAllowedOriginPatterns(List.of("*"))与setAllowCredentials(true)的组合需注意:允许凭证时,Access-Control-Allow-Origin不能是*,必须指定具体域名。虽然Spring的allowedOriginPatterns支持通配符,但需确保实际返回的响应头是https://example.com而非*,避免与Nginx的配置冲突。 - 清理Nginx配置中OPTIONS请求处理的重复代码:删除重复的
return 204;行,同时确保OPTIONS请求由Nginx直接返回204,不转发到Spring Boot,避免被鉴权逻辑拦截。
内容的提问来源于stack exchange,提问作者Joe
相关产品推荐
相关产品推荐

