解决跨域的方式

253 阅读3分钟

同源策略

协议 域名 端口

常见的跨域问题

cookie LocalStorage DOM元素 iframe ajax

实现跨域的方法

  • jsonp
  • cors
  • postMessage
  • document.domain
  • window.name
  • location.hash
  • http-proxy
  • nginx
  • websocket

jsonp

function jsonp({ url, params, cb }) {
    return new Promise((resolve, reject) => {
        window[cb] = function(data) {
            resolve(data);
            document.body.removeChild(script);
        };
        let script = document.cerateElement("script");
        params = { ...params, cb };
        const queryStr = Object.keys(params)
            .map(key => {
                return `${key}=${params[key]}`;
            })
            .join("&");
        script.src = `${url}?${queryStr}`;
        document.body.appendChild(script);
    });
}

缺点: 只能发送get请求; 容易受到xss攻击

cors

// express Server
const express = require("express");
const app = express();
app.use(express.static(__dirname));

const whitList = [
    "http://localhost:4000",
    "http://localhost:5000",
    "http://localhost:6000"
];

app.use(function(req, res, next) {
    let origin = req.headers.origin;
    if (whitList.includes(origin)) {
        // 设置允许跨域
        res.setHeader("Access-Control-Allow_origin", true);
        res.setHeader("Access-Control-Allow-Headers", "name"); // 允许携带哪些字段的请求头访问
        res.setHeader("Access-Control-Allow-Methods", "post"); // 允许的请求方法
        res.setHeader("Access-Control-Allow-Credentials", true); // 是否可以携带cookie
        res.setHeader("Access-Control-Allow-Expose-Headers", "name,token"); // 允许返回的请求头信息
        res.setHeader("Access-Control-Allow-Max-Age", 5000); // 预检的有效时间
        if (req.method === "OPTIONS") {
            // put 请求会发送两次
            res.end();
        }
    }
    next();
});

app.get("/getList", (req, res) => {
    const { id } = req.query;
    const body = {};
    res.end(body);
});

app.listen(300);

// AJAX
document.cookie = "token=kkhga2019";
xhr.withCredentials = true; // 携带cookie信息
let xhr = new XMLHttpRequest();
xhr.open("GET", "http://localhost:4000", true);
xhr.setRequestHeader("name", "appname");
xhr.onreadystatechange = function() {
    if (xhr.readyState === 4) {
        if ((xhr.status >= 200 && xhr.status <= 300) || xhr.status === 304) {
            const res = xhr.response;
        }
    }
};

缺点:

postMessage 页面之间的跨域通讯

<!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://localhsot:4000/b.html"
            frameborder="0"
            id="frame"
            onload="load()"
        ></iframe>
        <!-- a.html -->
        <script>
            function load() {
                let frame = document.getElementById("frame");
                frame.contentWindow.postMessage(
                    // 发
                    "getList",
                    "http://localhost:4000" // origin
                );
                // 收
                window.onmessage = function(e) {
                    // e.data
                };
            }
        </script>
        <!-- b.html -->
        <script>
            window.onmessage = function(e) {
                e.source.postMessage(data, e.origin);
            };
        </script>
    </body>
</html>

window.name

<!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://localhsot:4000/b.html"
            frameborder="0"
            onload="load()"
            id="iframe"
        ></iframe>
        <!-- a.html -->
        <script>
            let first = true;
            function load() {
                if (first) {
                    let iframe = document.getElementById("iframe");
                    iframe.src = "http://localhost:3000/b.html";
                    first = false;
                } else {
                    const data = iframe.contentWindow.name;
                }
            }
        </script>
        <!-- b.html -->
        <script>
            window.name = data;
        </script>
    </body>
</html>

location.hash

<!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>
        <!-- 路径后面的hash可以用来通讯 -->
        <iframe
            src="http://localhsot:4000/b.html#id=no101"
            frameborder="0"
            onload="load()"
            id="iframe"
        ></iframe>
        <!-- a.html -->
        <script>
            let first = true;
            function load() {
                let iframe = document.getElementById("iframe");
                iframe.src = "http://localhost:3000/b.html";
            }
            widnow.onhashchange = function() {};
        </script>
        <!-- b.html -->
        <script></script>
        <!-- c.html -->
        <script></script>
    </body>
</html>

document.domain

<!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://b.baidu.com/b.html"
            frameborder="0"
            onload="load()"
            id="iframe"
        ></iframe>
        <!-- a.html http://a.baidu.com -->
        <script>
            document.domane = "a.baidu.com";
            function load() {
                let iframe = document.getElementById("iframe");
                const a = iframe.contentWindow.a;
            }
        </script>
        <!-- b.html -->
        <script>
            ocument.domane = "a.baidu.com";
        </script>
    </body>
</html>

websocket

// websocket socket.io(解决兼容的库)
const socket = new WebSocket("ws://localhost:3000");
socket.onopen = function() {
    socket.send(data);
};
socket.onmessage = function(e) {
    const data = e.data;
};

// server
const express = requrie("express");
const app = express();
const WebSocket = require("ws");
const wss = new WebSocket.Server({ port: 3000 });
wss.on("connection", function(ws) {
    ws.on("message", function(data) {
        const data = data;
        ws.send(body);
    });
});

nginx

nginx文档