解决前端跨域的9种方式
一、什么是跨域?
浏览器有同源策略限制,协议、域名、端口号三者一样就是同源,三者只要有一个不同就是跨域。
二、9种方式
1、JSONP
2、CORS
3、http proxy
4、nginx反向代理
5、postMessage
6、socket.io
7、document.domain + iframe
8、document.name + iframe
9、location.hash + iframe
三、具体解决跨域的实现
1、JSONP (不常用)
原理:是利用<script>标签不存在跨域请求的限制
缺点:1、只能处理get请求 2、通过URl携带参数容易被劫持,不安全
举例:也可以动态的去创建<script>标签,原理在这随意发挥,只要是<script>标签即可
文件1:index.html
<script src="./index.jsonp.js"></script>
文件2:index.jsonp.js
$.ajax({ url: 'http://127.0.0.1:8001/list', method: 'get', dataType: 'jsonp', //=>执行的是JSONP的请求 success: res => { console.log(res); } });
后端接受请求参数,并返回客户端,客服端拿到数据后执行函数
2、CORS 跨域资源共享 (前端不需要做任何改变)
原理:后端设置可访问的请求源
缺点:1、设置具体地址,有局限性 2、设置多源(*)就不能允许携带cookie了
举例:
前端发送请求(axios是基于promise封装)
axios.get('http://127.0.0.1:3001/user/list') .then(result => { console.log(result); })
后端设置相关请求: *号为多源,也可以替换为具体地址
app.use((req, res, next) => { res.header("Access-Control-Allow-Origin", "*"); res.header("Access-Control-Allow-Credentials", true); res.header("Access-Control-Allow-Headers", "Content-Type,Content-Length,Authorization, Accept,X-Requested-With"); res.header("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,HEAD,OPTIONS"); if (req.method === 'OPTIONS') { res.send('OK!'); return; } next(); });
3、http proxy (常用)
原理:node + webpack + webpack-dev-server代理接口跨域
devServer: { port: 3000, progress: true, contentBase: './build', proxy: { '/': { target: 'http://127.0.0.1:3001', changeOrigin: true } } },
4、nginx反向代理 (不需要前端干啥)
server { listen 80; server_name www.chenfang.com; location / { proxy_pass www.chenfang.cn; #反向代理 proxy_cookie_demo www.chenfang.cn www.chenfang.com; add_header Access-Control-Allow-Origin www.chenfang.cn; add_header Access-Control-Allow-Credentials true; } }
5、postMessage
postMessage传递消息
onmessage接受消息
A页面 ``` <iframe src="http://www.chenfang.com/B.html"></iframe> <script> let iframe = document.querySelector('iframe'); iframe.onload = function () { iframe.contentWindow.postMessage('chenfang', 'http://www.chenfang.com/'); } window.onmessage = function (ev) { console.log(ev.data); } </script> ``` B页面 ``` window.onmessage = function (ev) { console.log(ev.data); ev.source.postMessage(ev.data+'@@', ev.origin); } ```
6、WebSocket协议跨域
前端处理 ``` <script src="./socket.io.js"></script> <script> let socket = io('http://127.0.0.1:3001/'); //=>连接成功处理 socket.on('connect', function() { //=>监听服务端消息 socket.on('message', function(msg) { console.log('data from server:' + msg); }); //=>监听服务端关闭 socket.on('disconnect', function() { console.log('server socket has closed!'); }); }); //=>发送消息给服务器端 socket.send("zhufengpeixun"); </script> ``` 服务器端处理 ``` //=>监听socket连接:server是服务器创建的服务 socket.listen(server).on('connection', function(client) { //=>接收信息 client.on('message', function(msg) { //=>msg客户端传递的信息 //... client.send(msg+'@@'); }); //=>断开处理 client.on('disconnect', function() { console.log('client socket has closed!'); }); }); ```
7、基于iframe的 document.domain
缺点:只能实现:同一个主域,不同子域之间的操作
父页面A http://www.chenfang.cn/A.html ``` <iframe src="http://school.chenfang.cn/B.html"></iframe> <script> document.domain = 'chenfang.cn'; var user = 'admin'; </script> ``` 子页面B http://school.chenfang.cn/B.html ``` <script> document.domain = 'chenfang.cn'; alert(window.parent.user); </script> ```
8、location.hash + iframe
A和C同源 A和B非同源 页面A ``` <iframe id="iframe" src="http://127.0.0.1:1002/B.html" style="display:none;"></iframe> <script> let iframe = document.getElementById('iframe'); //=>向B.html传hash值 iframe.onload=function(){ iframe.src = 'http://127.0.0.1:1002/B.html#msg=zhufengpeixun'; } //=>开放给同域C.html的回调方法 function func(res) { alert(res); } </script> ``` 页面B ``` <iframe id="iframe" src="http://127.0.0.1:1001/C.html" style="display:none;"></iframe> <script> let iframe = document.getElementById('iframe'); //=>监听A传来的HASH值改变,再传给C.html window.onhashchange = function () { iframe.src = "http://127.0.0.1:1001/C.html"+ location.hash; } </script> ``` 页面C ``` <script> //=>监听B传来的HASH值 window.onhashchange = function () { //=>再通过操作同域A的js回调,将结果传回 window.parent.parent.func(location.hash); }; </script> ```
9 、window.name + iframe
页面A ``` let proxy = function(url, callback) { let count = 0; let iframe = document.createElement('iframe'); iframe.src = url; iframe.onload = function() { if(count===0){ iframe.contentWindow.location = 'http://www.chenfang.cn/proxy.html'; count++; return; } callback(iframe.contentWindow.name); }; document.body.appendChild(iframe); }; //请求跨域B页面数据 proxy('http://www.chenfang.cn/B.html', function(data){ alert(data); }); ``` B页面 ``` window.name = 'chenfang'; ```