前端跨域2

前端跨域2

前端跨域解决方案(11种方案)

1.JSONP跨域解决方案的底层原理

script、img、link、iframe...
 <script src="https://cdn.bootcss.com/jquery/3.4.1/core.js"></script>
    // 这个就是因为script标签没有跨域限制,所以才能成功请求加载。

react中子组件想要修改父组件中的状态,也是传递一个回调函数给父组件,这个思想和JSONP的思想是一致的。

  • JSONP需要服务器的支持
  • 问题:JSONP只能处理GET请求?(放在?后面不安全,服务器返回的数据只在浏览器会直接执行,如果是木马修改?也会直接执行,不安全)

举例:
html页面: 1.jsonp.html

<!DOCTYPE html>
<html>

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0">
</head>

<body>
  <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
  <script src="./1.jsonp.js"></script>
</body>

</html>

1.jsonp.js

$.ajax({
    url: 'http://127.0.0.1:8001/list',
    method: 'get',
    dataType: 'jsonp', //=>执行的是JSONP的请求,这是jquery封装的ajax的功能
    success: res => {
        console.log(res);
    }
});

服务端配合:serverJSONP.js:

let express = require('express'),
    app = express();
app.listen(8001, _ => { // 监听8001端口
    console.log('OK!');
});
app.get('/list', (req, res) => {
    let {
        callback = Function.prototype // callback如果没有,默认为空的函数
    } = req.query;
    let data = {
        code: 0,
        message: '返回jsonp请求的结果'
    };
    res.send(`${callback}(${JSON.stringify(data)})`); //=>后端需要处理好这样的数据格式
});

2. CORS跨域资源共享

客户端正常发送请求,服务端设置相应的头信息。

  • 客户端(发送ajax/fetch请求)
axios.defaults.baseURL = 'http://127.0.0.1:8888';
axios.defaults.withCredentials = true;
axios.defaults.headers['Content-Type'] = 'application/x-www-form-urlencoded';
axios.defaults.transformRequest = function (data) {
  if (!data) return data;
  let result = ``;
  for (let attr in data) {
    if (!data.hasOwnProperty(attr)) break;
    result += `&${attr}=${data[attr]}`;
  }
  return result.substring(1);
};
axios.interceptors.response.use(function onFulfilled(response) {
  return response.data;
}, function onRejected(reason) {
  return Promise.reject(reason);
});
axios.defaults.validateStatus = function (status) {
  return /^(2|3)\d{2}$/.test(status);
}
  • 服务器设置相应的头信息(需要处理options试探性请求)
app.use((req, res, next) => {
    res.header("Access-Control-Allow-Origin", "http://localhost:8000"); // http://localhost:8000是允许跨域请求的地址,如果允许很多地址跨域请求,设置为"*"
    //=>*(就不能在允许携带cookie了) 具体地址
    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实现跨域请求

http proxy => webpack webpack-dev-server
修改:webpack.config.js

let path = require('path');
let HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
    mode: 'production',
    entry: './src/index.js',
    output: {
        filename: 'bundle.min.js',
        path: path.resolve(__dirname, 'build')
    },
    devServer: {
        port: 3000,
        progress: true,
        contentBase: './build',
        proxy: { // => 以'/'开始的请求,就把请求路径转到 target
            '/': {
                target: 'http://127.0.0.1:3001',
                changeOrigin: true // => 允许跨域
            }
        }
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: './src/index.html',
            filename: 'index.html'
        })
    ]
};

4. 基于post message实现跨域处理

postMessage方法允许来自不同源的脚本采用异步方式进行有限的通信,可以实现跨文本档、多窗口、跨域消息传递。
https://developer.mozilla.org…
A页面

<iframe src="http://www.github.com/B.html"></iframe>
<script>
    let iframe = document.querySelector('iframe');
    // onload 事件会在页面或图像加载完成后立即发生, iframe 中的onload事件会在整个文档加载完成后执行。
    iframe.onload = function () {
        iframe.contentWindow.postMessage('github', 'http://www.github.com/');
        // 'http://www.github.com/' 接受 'github' 参数
    }
    
    window.onmessage = function (ev) {
        console.log(ev.data);
    }
</script>

B 页面

// 接收方监听message事件
window.onmessage = function (ev) {
    console.log(ev.data);
    ev.source.postMessage(ev.data+'@@', ev.origin);
}

** 5. nginx反向代理 => 不需要前端做什么

www.github.cn -> www.github.com

#proxy服务器
server {
    listen       80;
    server_name  www.github.com;
    location / {
        proxy_pass   www.github.cn; #反向代理
        proxy_cookie_demo www.github.cn www.github.com;
        add_header Access-Control-Allow-Origin www.github.cn;
        add_header Access-Control-Allow-Credentials true;
    }
}

** 6. 基于 iframe 的跨域解决方案**

三种:window.name / document.domin / location.hash

① 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.github.cn/proxy.html';
          count++;
          return;
        }
        callback(iframe.contentWindow.name);
    };
    document.body.appendChild(iframe);
};

//请求跨域B页面数据
proxy('http://www.github.cn/B.html', function(data){
    alert(data);
});

B页面

window.name = 'github';

proxy.html是空页面

② 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=github';
    }
    
    //=>开放给同域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>

③ document.domain + iframe
只能实现:同一主域,不同子域之间的操作
v.qq.com
sports.qq.com
父页面A http://www.github.cn/A.html

<iframe src="http://school.github.cn/B.html"></iframe>
<script>
    document.domain = 'github.cn';  // 相同的主域名
    var user = 'admin';
</script>

子页面B http://school.github.cn/B.html

<script>
    document.domain = 'github.cn';
    alert(window.parent.user);
</script>

** 7. 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("github");
</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!');
    });
});
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

简 。单

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值