CORS setting not working when allow credentials or 当 credentials true 时跨域配置无效的问题
lanlin opened this issue · 1 comments
情景
通过浏览器 XHR 发起跨域请求,而且需要携带 cookie 或者 Authentication 信息时。
如果发现一旦设置 Access-Control-Allow-Credentials: true
就无法实现正常跨域时,可以参考本文。
解决方法及原因分析 (以 Nginx 为例)
1. 浏览器的原因
一般你在网上找到的设置 nginx 跨域的文章,很多都是类似如下的 wildcard 配置
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' '*';
add_header 'Access-Control-Allow-Headers' '*';
简单来说就是为了图省事,用星号来表示允许来自所有源、方法以及头信息的跨域请求。
这样的设置,在不需要携带 cookie 和 authentication 的信息时,除了不安全以外,倒是能正常实现跨域。
但是,现在浏览器对于跨域的限制越来越严格。如果配置中包含 add_header 'Access-Control-Allow-Credentials' 'true';
则上面三项配置必须设置其具体的值,如果任意一项包含星号,则跨域请求仍然会失败。
2. Nginx 设置原因
如果你的 Nginx 是对后端程序进行反向代理,比如反向代理 PHP。
此时,由于后端程序返回的数据中包含有头信息。Nginx 一般会直接将该头信息返回给浏览器,而不会将你配置在 Nginx 中的跨域信息添加到其中。
add_header 'Access-Control-Allow-Origin' '*' always;
add_header 'Access-Control-Allow-Methods' '*' always;
add_header 'Access-Control-Allow-Headers' '*' always;
这个时候你需要像上面那样,在 add_header
的末尾加上 always
来强制 Nginx 在返回数据时携带上跨域头信息。
3. 程序的原因
理论上,你在 Nginx 中配置的跨域头信息,只有在上游程序返回“正常”的头信息状态码时,才会被携带返回给浏览器。
一般来说,设置 always
应该对这种情况也起作用。
如果仍然无效,那么建议你直接把 Nginx 的跨域设置全部删除,直接在上游程序中返回相应的头信息,完全不用理会 Nginx 配置。
其他补充
1. 浏览器如何处理跨域请求
浏览器在处理 XHR 跨域请求时,会分两步来进行。
第一步,发送一个 OPTIONS
的预检请求来询问服务器对于跨域的授权策略。
此时,头信息会携带 access-control-request-headers
以及 access-control-request-method
这两项。
分别表示下一步将会被携带的头信息项,以及将采用的提交方法。而 Nginx 返回中的 Access-Control-Allow-Headers
项必须包含对应的项,Access-Control-Allow-Methods
中也必须包含对应的方法,表示允许第二步以这中方式提交。
如果返回的授权策略不符合以上要求,则第二步的请求会被浏览器自动取消。
第二步,浏览器正式发出请求。
此时,Nginx 返回的头信息中仍然必须包含 Allow Origin、Allow Headers 以及 Allow Methods 这三项。
不然,浏览器在收到返回数据后,会自动将该请求标记为失败,而不会将返回数据传递给 JS。
需要注意的是,OPTIONS 请求可能不会每次都出现,原因在于浏览器是否对其进行了缓存。
而且,理论上第二步除 Allow Origin 以外, Allow Headers 和 Allow Methods 的值设置为空字符串也不影响。
2. Nginx Plus
Nginx 是有个 Plus 版的。Plus 版是收费的,但是有很多动态模块可以使用,提供了一些更强大的功能。
比如,普通版的 Nginx 是没有提供一个变量来获取所有请求头的,但是在 Plus 中却可以通过安装其他模块来实现这一点。
以 ngx_http_lua_module
模块为例。
在 Nginx Plus 中,安装了该模块后,就能用如下代码来获取请求中的头信息。
set_by_lua $request_headers '
local t = {}
local h = ngx.req.get_headers()
for k, _ in pairs(h) do
t[#t+1] = k
end
return table.concat(t, ",")
';
3. 另类的奇葩
有一个叫 Openresty 的玩意,通过扩展 Nginx, 把 LuaJIT 跟 Nginx 绑定到一起。
搞出来一个 框架+WEB服务器 的奇葩产品,或者叫框架内置服务器?我也不懂,反正据说罗锤子给他们捐过。
敢信去的朋友可以去尝试一下。