Skip to main content

跨域

同源策略是浏览器的安全策略。如果两个页面的协议、端口和主机都相同,则它们具有相同的源,非同源页面交互会受到限制

JSONP

只限于 get 请求。利用 script 标签(还有img、link、form)没有跨域限制的性质,通过 script 标签指向一个需要访问的地址并提供一个回调函数来接收数据

// 这里用promise封装jsonp函数
jsonp("http://localhost:3000/api/test", {
params: {
name: "JacksonZhou",
},
jsonp: "getName", // 回调函数
}).then((res) => {
res = JSON.parse(res);
let index = document.getElementById("index");
index.innerHTML = res.desc;
});
function jsonp(url, options = {}) {
let time = options.time || 5000;
let paramStr = "";
if (options.params && JSON.stringify(options.params) !== "{}") {
Object.keys(options.params).forEach((key) => {
paramStr += `&${key}=${options.params[key]}`;
});
}
if (!options.jsonp) {
throw new Error("请定义一个回调函数");
}
return new Promise((resolve, reject) => {
var oHead = document.getElementsByTagName("head")[0];
let script = document.createElement("script");
oHead.appendChild(script);
script.src = `${url}?callback=${options.jsonp}${paramStr}`;
window[options.jsonp] = function (data) {
resolve(data);
// 要清除script标签
oHead.removeChild(script);
script = null;
clearTimeout(timer);
};
// 超时处理,利用超时判断请求成功与否
let timer = setTimeout(() => {
// 要清除script标签
oHead.removeChild(script);
script = null;
reject("请求超时");
}, 5000);
});
}

后台服务器包装回调函数并返回(基于 koa2),前端接收到 js 代码会执行,也就直接执行了回调函数

// 测试
async function test(ctx) {
let query = ctx.query;
let data = {
id: Date.now().toString(16),
desc: `Hello, ${query.name}`,
};
let cb = `${query.callback}('${JSON.stringify(data)}')`;
ctx.body = cb;
}

跨域资源共享CORS

服务端设置 Access-Control-Allow-Origin 就可以开启 CORS。 该属性表示哪些域名可以访问资源,如果设置通配符则表示所有网站都可以访问资源。若要带 cookie 请求:前后端都需要设置

// 前端设置是否带cookie,原生设置
xhr.withCredentials = true;
// axios设置
axios.defaults.withCredentials = true;
// nodejs跨域后台设置
res.writeHead(200, {
"Access-Control-Allow-Credentials": "true", // 后端允许发送Cookie
"Access-Control-Allow-Origin": "http://www.domain1.com", // 允许访问的域(协议+域名+端口)
/*
* 此处设置的cookie还是domain2的而非domain1,因为后端也不能跨域写cookie(nginx反向代理可以实现),
* 但只要domain2中写入一次cookie认证,后面的跨域接口都能从domain2中获取cookie,从而实现所有的接口都能跨域访问
*/
"Set-Cookie": "l=a123456;Path=/;Domain=www.domain2.com;HttpOnly", // HttpOnly的作用是让js无法读取cookie
});

nginx 反向代理跨域

原理是规避同源策略,可以用 nginx 作为代理服务器,实现数据的转发

server {
listen 9000; #配置第一台服务器
server_name localhost;

#charset koi8-r;

#access_log logs/host.access.log main;

location /api/ {
rewrite ^/api/(.*)$ /$1 break;
proxy_pass http://www.serverA.com; # API Server #将真正的请求代理到serverA,即真实的服务器地址,ajax的url为/api/user/1的请求将会访问http://www.serverA.com/user/1
}

#error_page 404 /404.html;

# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}

也可以用 nodejs 做代理服务器,原理和上面相同。

postMessage

多用于前端页面之间的通讯

应用场景:

  1. 页面和其打开的新窗口的数据传递
  2. 多窗口之间消息传递
  3. 页面与嵌套的 iframe 消息传递
  4. 上面三个场景的跨域数据传递
<!--a.html发送数据和接收数据-->
<iframe
id="iframe"
src="http://www.domain2.com/b.html"
style="display:none;"
></iframe>
<script>
var iframe = document.getElementById("iframe");
iframe.onload = function () {
var data = {
name: "aym",
};
// 向domain2传送跨域数据
iframe.contentWindow.postMessage(
JSON.stringify(data),
"http://www.domain2.com"
);
};

// 接受domain2返回数据
window.addEventListener(
"message",
function (e) {
alert("data from domain2 ---> " + e.data);
},
false
);
</script>
<!--b.html接收和返回数据-->
<script>
// 接收domain1的数据
window.addEventListener(
"message",
function (e) {
alert("data from domain1 ---> " + e.data);

var data = JSON.parse(e.data);
if (data) {
data.number = 16;

// 处理后再发回domain1
window.parent.postMessage(
JSON.stringify(data),
"http://www.domain1.com"
);
}
},
false
);
</script>

WebSocket

websocket 实现了浏览器与服务器全双工通信,同时允许跨域通讯

// 前端
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.0.3/socket.io.js"></script>
...
let io = io.connect(url)
io.on('data', function (data) {
console.log(data)
})
// 服务器
let io = require('socket.io')(8888) // 监听8888端口号
io.on('connection', function (data){
client.emit('data', 'Hello, JacksonZhou')
} )