跨域有多种解决方案,如jsonp 、子域跨域,设置同主域domain 、http头cors设置,反向代理等。
跨域解决方案
由于这一次,我们要做网关跨域,会碰到各种各样的情况,为了深度解决跨域的问题,我们采用cors头设置,他不会受到主域相同的限制策略,可以算是我们网关跨域的终极解决方案。
http cors头设置分为两种情况
- 不带cookie跨域
- 带cookie跨域
不带cookie跨域,只需要服务端设置Access-Control-Allow-Origin:*即可
带cookie跨域,需要前端与服务端同时设置
前端设置,分为jquery fetch axios三种方式
xhrFields: {withCredentials: true} // jQuery 1.5.1
credentials: 'include' // ES6 fetch()
withCredentials: true // axios:
服务端设置
res.writeHead(200, {
"content-type": "text/javascript",
'Access-Control-Allow-Credentials': 'true',
"Access-Control-Allow-Methods": "POST, GET",
'Access-Control-Allow-Origin': 'http://npc.yang800.com', //此处必须是明确的域名,不能是*
'Set-Cookie': [
`login=true;` ,
`user=333;`
],
});
但丁云网关设置
目前,我们的后台登陆与权限设置,都分为供应商端,和小二端。所有我们的登陆,和接口登陆判断,都需要以路径的形式分开。如/supplier/api1 、/supplier/api2 、 /admin/api1 、/admin/api1
因此,我们接口登陆后,cookie的形式,以path的形式去设置
'Set-Cookie': [
`login=true;Path=/supplier;` ,
`user=333;Path=/supplier;`
]
'Set-Cookie': [
`login=true;Path=/admin;`,
`user=666;Path=/admin;`
],
测试用例
下面附上完整的测试用例。我们通过 host 绑定 127.0.0.1 api.yang800.com这个域名
前端
import React, { useState } from 'react';
import ReactDom from 'react-dom'
import axios from 'axios'
/**
*
* jQuery 1.5.1 xhrFields: {withCredentials: true}
ES6 fetch() credentials: 'include'
axios: withCredentials: true
*/
function App(){
var [data,setData] = useState('');
return (
<div>
<div style={{height:'200px' , border:'solid 1px #ddd'}}>{data}</div>
<button onClick={async() => {
let res = await axios.request({
url : 'http://api.yang800.com:3000/login1' ,
crossDomain: true,
withCredentials: true
})
setData(JSON.stringify(res.data))
}}>scm登陆</button>
<button onClick={async() => {
let res = await axios.request({
url: 'http://api.yang800.com:3000/api1/get-user',
crossDomain: true,
withCredentials: true
})
setData(JSON.stringify(res.data))
}}>获取scm数据</button>
<button onClick={async () => {
let res = await axios.request({
url: 'http://api.yang800.com:3000/loginout1',
crossDomain: true,
withCredentials: true
})
setData(JSON.stringify(res.data))
}}>退出scm登陆</button>
<button onClick={async () => {
let res = await axios.request({
url: 'http://api.yang800.com:3000/login2',
crossDomain: true,
withCredentials: true
})
setData(JSON.stringify(res.data))
}}>npc登陆</button>
<button onClick={async () => {
let res = await axios.request({
url: 'http://api.yang800.com:3000/api2/get-user',
crossDomain: true,
withCredentials: true
})
setData(JSON.stringify(res.data))
}}>获取npc数据</button>
<button onClick={async () => {
let res = await axios.request({
url: 'http://api.yang800.com:3000/logout2',
crossDomain: true,
withCredentials: true
})
setData(JSON.stringify(res.data))
}}>退出npc登陆</button>
</div>
)
}
ReactDom.render(<App /> , document.getElementById('root'));
服务端 nodejs
var http = require("http");
http.createServer(function (req, res) {
var origin = req.headers.origin;
var url = req.url;
var method = req.method;
console.log(origin , url , method)
if(url == '/login1'){
res.writeHead(200, {
"content-type": "text/javascript",
'Access-Control-Allow-Credentials': 'true',
"Access-Control-Allow-Methods": "POST, GET",
'Access-Control-Allow-Origin': origin || 'http://api.yang800.com',
'Set-Cookie': [
`login=true;Path=/api1;` ,
`user=333;Path=/api1;`
],
});
var data = {
code : 0 ,
data : 'login success'
}
res.write(JSON.stringify(data));
res.end();
return ;
}
if (url == '/loginout1') {
res.writeHead(200, {
"content-type": "text/javascript",
'Access-Control-Allow-Credentials': 'true',
"Access-Control-Allow-Methods": "POST, GET",
'Access-Control-Allow-Origin': origin || 'http://api.yang800.com',
'Set-Cookie': [
`login=true;Path=/api1;Max-Age=-1`,
`user=333;Path=/api1;Max-Age=-1`
],
});
var data = {
code: 0,
data: 'logout success'
}
res.write(JSON.stringify(data));
res.end();
return;
}
if (url == '/login2') {
res.writeHead(200, {
"content-type": "text/javascript",
'Access-Control-Allow-Credentials': 'true',
"Access-Control-Allow-Methods": "POST, GET",
'Access-Control-Allow-Origin': origin || 'http://api.yang800.com',
'Set-Cookie': [
`login=true;Path=/api2;`,
`user=666;Path=/api2;`
],
});
var data = {
code: 0,
data: 'login success'
}
res.write(JSON.stringify(data));
res.end();
return;
}
if (url == '/logout2') {
res.writeHead(200, {
"content-type": "text/javascript",
'Access-Control-Allow-Credentials': 'true',
"Access-Control-Allow-Methods": "POST, GET",
'Access-Control-Allow-Origin': origin || 'http://api.yang800.com',
'Set-Cookie': [
`login=true;Path=/api2;Max-Age=-1`,
`user=666;Path=/api2;Max-Age=-1`
],
});
var data = {
code: 0,
data: 'logout success'
}
res.write(JSON.stringify(data));
res.end();
return;
}
if (url.indexOf('/api1') == 0 || url.indexOf('/api2') == 0){
var cookie = req.headers.cookie;
if (cookie && cookie.indexOf('login=true') > -1){
var data = {
code : 0,
cookie: cookie ,
message : '登陆成功'
};
}else{
var data = {
code: -1001,
cookie: cookie,
message: '未登陆'
};
}
res.writeHead(200, {
"content-type": "text/javascript",
'Access-Control-Allow-Credentials': 'true',
"Access-Control-Allow-Methods": "POST, GET",
'Access-Control-Allow-Origin': origin || 'http://api.yang800.com',
});
res.write(JSON.stringify(data));
res.end();
return;
}
}).listen(3000);
后记
我们的跨域方案,都是基于第三方cookie,但目前safari高版本,默认设置,已经禁用第三方cookie ,所以,在高版本的safari上面,我们的跨域方案,会失败。
在这里,我们只有手动打开阴止跨站跟踪,才能安全的使用第三方跨域