跨域请求如何设置cookie的问题
一、问题背景
跨域种cookie的问题就是,比如a.123.com跨域访问b.123.com/request,b.123.com服务器使用nginx允许跨域,Access-Control-Allow-Origin:*
如果a、b服务不在同一个服务器,前台页面请求报错信息为:
Access to XMLHttpRequest at 'http://b.123.com'
from origin 'http://a.123.com' has been blocked by CORS policy:
The value of the 'Access-Control-Allow-Origin' header in the response
must not be the wildcard '*' when the request's credentials mode is 'include'.
The credentials mode of requests initiated by the XMLHttpRequest
is controlled by the withCredentials attribute.
对应ajax请求为:
$.ajax({
url : 'http://b.123.com/request',
data : data,
dataType: 'json',
type : 'POST',
xhrFields: {
withCredentials: true
},
crossDomain: true,
...
此时,应取消nginx设置的跨域*,改成代码端设置。且代码服务器端通过在响应 header 中设置
response.setHeader("Access-Control-Allow-Credentials", "true");
来运行客户端携带证书式访问。通过对 Credentials 参数的设置,就可以保持跨域 Ajax 时的 Cookie。
服务器端 Access-Control-Allow-Credentials = true
时,Access-Control-Allow-Origin
的值不能为 '*'
,应设置为发起请求的地址。
// a.com发来的请求
response.setHeader("Access-Control-Allow-Origin", a.123.com);
b服务器在设置cookie时,需设置
cookie.setPath("/");
cookie.setDomain("123.com");
否则设置的cookie无法生效。
二、跨域请求怎么携带cookie
呢?
解决跨域携带cookie问题
1、在前端请求的时候设置request对象的属性withCredentials为true
什么是withCredentials
?XMLHttpRequest.withCredentials 属性是一个Boolean
类型,它指示了是否该使用类似cookies,authorization headers(头部授权)或者TLS客户端证书这一类资格证书来创建一个跨站点访问控制(cross-site Access-Control
)请求。在同一个站点下使用withCredentials属性是无效的。
如果在发送来自其他域的XMLHttpRequest请求之前,未设置withCredentials
为true,那么就不能为它自己的域设置cookie值。而通过设置withCredentials
为true获得的第三方cookies,将会依旧享受同源策略,因此不能被通过document.cookie或者从头部相应请求的脚本等访问。
// 修改跨域请求的代码
crossButton.onclick = function () {
axios({
withCredentials: true, // ++ 新增
method: "get",
url: "http://localhost:8003/anotherService",
}).then((res) => {
console.log(res);
});
};
这个时候再去发送一个跨域请求,你会发现依旧报错,但是我们仔细看下报错,意思是需要设置header的Access-Control-Allow-Origin
属性:
Access-Control-Allow-Origin
这个就是上面问题背景的问题
// 在所有路由前增加,可以拦截所有请求
app.all("*", (req, res, next) => {
res.header("Access-Control-Allow-Origin", "http://localhost:8000");
next();
});
但是还是会报错:修改完之后再次发送一个跨域请求,你会发现,又报错了,但是跟之前报的错不一样了,意思大概就是Access-Control-Allow-Credentials
这个属性应该设置为true
,但是显示得到的是个''
:
3、在服务端设置Access-Control-Allow-Credentials
// 在所有路由前增加,可以拦截所有请求
app.all("*", (req, res, next) => {
res.header("Access-Control-Allow-Origin", "http://localhost:8000");
res.header("Access-Control-Allow-Credentials", "true"); // ++ 新增
next();
});
这样即可成功。