【发布时间】:2018-03-14 22:02:25
【问题描述】:
我在 Nginx 反向代理后面有一个 Spring Boot 服务器,我使用来自 React 应用程序的 fetch 访问它。前端是从另一个端口提供的,所以我必须在服务器上启用 CORS。在大多数情况下,这很有效,但我的用户中约有 1% 收到 403 响应 OPTIONS 预检请求,我需要帮助找出原因。我遇到的最大问题是我无法在我的任何机器上复制该问题。
Spring Boot CORS 配置:
@Bean
public FilterRegistrationBean corsFilter() {
CorsConfiguration config = new CorsConfiguration();
config.addAllowedOrigin("https://example.com");
config.addAllowedHeader("*");
config.addAllowedMethod("GET");
config.addAllowedMethod("PUT");
config.addAllowedMethod("POST");
config.addAllowedMethod("DELETE");
config.addAllowedMethod("PATCH");
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
return bean;
}
Nginx 配置(3000 是 NodeJS 服务前端,3001 是 Spring Boot):
server {
...
location / {
proxy_pass http://localhost:3000;
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/v1/ {
proxy_pass http://localhost:3001;
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;
}
...
}
Nginx 日志格式(为了清楚起见,我删除了一些部分):
"$request" $status "$http_referer" "$http_user_agent"
通过查看 Nginx access.log,我确定了显示 403 的 2 种类型的日志行:
"OPTIONS /api/v1/oauth/token?grant_type=password&username=user%40example.com&password=****" 403 "https://www.example.com/login" "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36"
表示运行 Chrome 61 的 Windows 7,并且
"OPTIONS /api/v1/oauth/token?grant_type=password&username=user%40example.com&password=****" 403 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko"
表示运行 IE11 的 Windows 7。
使用相同操作系统和浏览器设置的其他用户不会遇到任何问题。
使用 fetch 手动发送数据:
URL: https://example.com:3001/api/v1/oauth/token?grant_type=password&username=user%40example.com&password=****
Method: POST
Headers:
Authorization: Basic XXXXXXXXXXX=
Content-Type: application/x-www-form-urlencoded
Body: undefined
工作用户的预检请求中的实际参数(来自 Chrome 控制台):
请求标头:
OPTIONS /api/v1/oauth/token?grant_type=password&username=user%40example.com&password=**** HTTP/1.1
Host: https://example.com:3001
Connection: keep-alive
Access-Control-Request-Method: POST
Origin: https://example.com:3000
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.79 Safari/537.36
Access-Control-Request-Headers: authorization
Accept: */*
Referer: https://example.com:3000/login
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.8,sv;q=0.6
响应标头:
HTTP/1.1 200
Access-Control-Allow-Origin: https://example.com:3000
Vary: Origin
Access-Control-Allow-Methods: GET,PUT,POST,DELETE,PATCH
Access-Control-Allow-Headers: authorization
Content-Length: 0
Date: Tue, 03 Oct 2017 16:01:37 GMT
我的猜测是我发送获取请求的方式有问题,或者我错误地配置了标头。但是到目前为止我还没有解决它。
任何帮助将不胜感激。
【问题讨论】: