1. 什么是跨域
简单的来说就是:
在A域名下向B域名发起了 request。
一个完整的URL应该包含 http(协议)://www.baidu.com(域名):80(端口)/index.html(路由)。
同源策略 就是指的 协议,域名,端口必须保持一致。任何不符合同源策略的请求 request 都属于跨域了。
2.通过jsonp跨域
index.html页面中
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<script>
function jsonp({url, params, cb}){
let script = document.createElement('script');
window[cb] = function( data ){
resolve(data);
document.body.removeChild(script);
}
params = {...params, cb} //wd=b&cb=show
let arrs = [];
for(let key in params){
arrs.push(`${key}=${params[key]}`);
}
script.src = `${url}?${arrs.join('&')}`;
document.body.appendChild(script);
}
//只能发送get 请求,不支持 post put delete
//不安全 xss攻击 不采用
jsonp({
url: 'http://localhost:3000/say',
params: {wd: 'hello'},
cb: 'show' //回调函数
}).then(data => {
console.log(data);
});
</script>
</body>
复制代码
server.js
let express = require('express');
let app = express();
app.get('/say', function(req, res){
let {wd, cb} = req.query;
res.end(`${cb}('world')`); //返回 'show('world')'
});
复制代码
3. 通过CORS跨域
cors.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
hello cors.html
<script>
let xhr = new XMLHttpRequest;
document.cookie = 'nam=zfpx';
xhr.withCredentials = true;
xhr.open('PUT', 'http://localhost:4000/getData', true);
xhr.setRequestHeader('name','zfpx');
xhr.onreadystatechange = function(){
if(xhr.readyState === 4){
if(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304){
console.log(xhr.response);
console.log(xhr.getResponseHeader('name'));
}
}
}
xhr.send();
</script>
</body>
复制代码
- cors.html.server.js
let express = require('express');
let app = express();
app.use(express.static(__dirname));
app.listen(3000);
复制代码
- cors.target.js
let express = require('express');
let app = express();
let whitList = ['http://localhost:3000']; //用来访问 cors.html的页面
app.use(function(req, res, next){
let origin = req.headers.origin;
if(whitList.includes(origin)){
//设置允许哪个源访问我
res.setHeader('Access-Control-Allow-Origin', origin);
//允许携带哪个请求头访问我
res.setHeader('Access-Control-Allow-Headers', 'name');
//允许哪个方法访问我
res.setHeader('Access-Control-Allow-Methods', 'PUT');
//允许携带cookie, 与前端 xhr.withCredentials = true; 相呼应
res.setHeader('Access-Control-Allow-Credentials', true);
//允许的有效期: 第二次发送options方法的时间间隔
res.setHeader('Access-Control-Max-Age', 60); //60s
//允许设置的返回头 与前端 console.log(xhr.getResponseHeader('name'));相呼应
res.setHeader('Access-Control-Expose-Headers', 'name');
if(req.method === 'OPTIONS'){
res.end(); //浏览器在发起put请求前会发送一个options方法,来验证允许访问的各种参数
}
}
});
app.put('/getData', function(req, res){
console.log(req.headers);
res.setHeader('name','jw');
res.end('hello');
});
app.use(express.static(__dirname));
app.listen(4000)
复制代码
从 http://localhost:3000 向 http://localhsot:4000 发送put 请求。
4. 通过postMessage跨域
通过 localhost:3000 访问 a.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<iframe src="http://localhost:4000/b.html" frameborder="0" id="frame" onload="load()"></iframe>
<script>
function load() {
let iframe = document.getElementById('fram');
frame.contentWindow.postMessage('hello', 'http://localhost:4000');
window.onmessage = function(e){
console.log(e.data); //打印 world
}
}
</script>
</body>
复制代码
通过localhost:4000返回 b.html
....
<script>
window.onmessage = function(e){
console.log(e.data); // 打印 hello
e.source.postMessage('world', e.origin);
}
</script>
....
复制代码
5. 通过window.name 跨域
默认情况下 window.name 为 "" 有a.html, b.html, c.html. 其中a.html 和 b.html 通过http://localhost:3000访问,c.html通过 http://localhost:4000访问.
a.html
....
a和b是同域的 http://localhost:3000
c是独立的 http://localhost:4000
a获取c的数据
a先引用c c把值放到window.name, 把a引用的地址改到b
<iframe src="http://localhost:4000/c.html" frameborder="0" onload="load()" id="iframe"></iframe>
<script>
let first = true
function load() {
if(first){
let iframe = document.getElementById('iframe');
iframe.src = 'http://localhost:3000/b.html';
first = false;
}else{
console.log(iframe.contentWindow.name);
}
}
....
复制代码
c.html
...
<script>
window.name = 'hello a.html'
</script>
...
复制代码
b.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
</body>
</html>
复制代码
a.html 中的控制台中会打印出 hello a.html
6.通过hash方式跨域
a.html
...
<!-- 路径后面的hash值可以用来通信 -->
<!-- 目的a想访问c -->
<!-- a给c传一个hash值 c收到hash值后 c把hash值传递给b b将结果放到a的hash值中-->
<iframe src="http://localhost:4000/c.html#from_a"></iframe>
<script>
window.onhashchange = function () {
console.log(location.hash); //打印from_c
}
</script>
...
复制代码
b.html
...
<script>
window.parent.parent.location.hash = location.hash
</script>
...
复制代码
c.html
...
<script>
console.log(location.hash); //打印from_a
let iframe = document.createElement('iframe');
iframe.src = 'http://localhost:3000/b.html#form_c';
document.body.appendChild(iframe);
</script>
...
复制代码
7.通过domain方式跨域
a.html
...
<!-- 域名 一级域名二级域名 -->
<!-- www.baidu.com -->
<!-- viode.baidu.com -->
<!-- a是通过 http://a.zf1.cn:3000/a.html -->
helloa
<iframe src="http://b.zf.cn:3000/b.html" frameborder="0" onload="load()" id="frame"></iframe>
<script>
document.domain = 'zf.cn'
function load() {
console.log(frame.contentWindow.a);
}
</script>
...
复制代码
b.html
...
hellob
<script>
document.domain = 'zf.cn'
var a = 100;
</script>
...
复制代码
8.通过websocket方式跨域
socket.html
...
let socket = new WebSocket('ws://localhost:3000');
socket.onopen = function(){
socket.send('hello');
}
socket.onmessage = function(e){
console.log(e.data); //打印world
}
...
复制代码
server.js
let express = require('express');
let app = express();
let WebSocket = require('ws');
let wss = new WebSocket.Server({port: 3000});
wss.on('connection', function(ws){
console.log(data); // 打印 hello
ws.send('world');
});
复制代码
9.通过nginx方式跨域
a.html
...
<script>
let xhr = new XMLHttpRequest;
xhr.open('get','http://localhost/a.json',true);
xhr.onreadystatechange = function () {
if(xhr.readyState === 4){
if(xhr.status>=200 && xhr.status < 300 || xhr.status ===304){
console.log(xhr.response);
console.log(xhr.getResponseHeader('name'));
}
}
}
xhr.send();
</script>
...
复制代码
nginx.conf
location / {
// 没有配置OPTIONS的话,浏览器如果是自动识别协议(http or https),那么浏览器的自动OPTIONS请求会返回不能跨域
if ($request_method = OPTIONS ) {
add_header Access-Control-Allow-Origin "$http_origin";
add_header Access-Control-Allow-Methods "POST, GET, PUT, OPTIONS, DELETE";
add_header Access-Control-Max-Age "3600";
add_header Access-Control-Allow-Headers "Origin, X-Requested-With, Content-Type, Accept, Authorization";
add_header Access-Control-Allow-Credentials "true";
add_header Content-Length 0;
add_header Content-Type text/plain;
return 200;
}
add_header 'Access-Control-Allow-Origin' '$http_origin';
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Methods' 'GET, PUT, POST, DELETE, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'Content-Type,*';
proxy_pass http://127.0.0.1:8080;
}
}
复制代码
nginx的方式有些类似cors。
10.小结
平时工作中可能熟知cors 和 jsonp, 这里大概将其原理实现了。以后再遇到面试官问你。就可以淡定的告诉他。然后还可以介绍其他的几种方式。用跨域的方式震慑住他。哈哈哈~~