前端跨域解決方案(11種方案詳細筆記)

前端跨域解決方案(11種方案詳細筆記)

1、跨域問題的產生及其價值意義

跨域(非同源策略請求),是由瀏覽器的安全機制引發的。
三者都同樣就是同源,只要有一個不一樣就是跨域css

前端(跨域)的發展:

一、沒有專門的前端,由後端一塊兒作。

二、先後端分開,部署的時候部署在同一個服務器,這樣就不存在跨域問題。可是,開發的時候先後端是分開的,仍是會存在跨域,怎麼辦呢?有的公司要求前端也用idea進行開發,也就是前端代碼外面包一層java或其它後端代碼,運行的時候也要運行後端服務,這樣的作法給前端開發加大了難度。html

  • 同源策略請求 ajax / fetch
  • 跨域傳輸

部署到web服務器上:同源策略前端

  • xampp 修改本地的host文件
127.0.0.1:1234  http://api.qq.com/

http://127.0.0.1:1234/index.html
http://api.qq.com/getData


三、先後端徹底分離,分開開發,分開部署web服務器,data服務器,圖片服務器。 第三方開源的數據接口也會引發跨域(這樣的狀況很是多)。java

2、JSONP跨域解決方案的底層原理

  • script
  • img
  • link
  • iframe
  • ...
    =>不存在跨域請求的限制
<script src="https://cdn.bootcss.com/jquery/3.4.1/core.js"></script>
// 這個就是由於script標籤沒有跨域限制,因此才能成功請求加載。

JSONP核心原理圖:
clipboard.png
react中子組件想要修改父組件中的狀態,也是傳遞一個回調函數給父組件,這個思想和JSONP的思想是一致的。react

  • 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)})`); //=>後端須要處理好這樣的數據格式
});

3、CORS跨域資源共享

客戶端正常發送請求,服務端設置相關的頭信息。jquery

  • 客戶端(發送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();
});

4、基於http proxy實現跨域請求

http proxy  =>webpack webpack-dev-server

修改webpack.config.jswebpack

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'
        })
    ]
};

5、基於post message實現跨域處理

https://developer.mozilla.org...ios

A頁面git

<iframe src="http://www.github.com/B.html"></iframe>
<script>
    let iframe = document.querySelector('iframe');
    iframe.onload = function () {
        iframe.contentWindow.postMessage('github', 'http://www.github.com/');
    }
    window.onmessage = function (ev) {
        console.log(ev.data);
    }
</script>

B頁面github

window.onmessage = function (ev) {
    console.log(ev.data);
    ev.source.postMessage(ev.data+'@@', ev.origin);
}

6、ngnix反向代理 =>不須要前端作什麼

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;
    }
}

7、基於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>

8、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!');
    });
});
相關文章
相關標籤/搜索