九种跨域解决方案

同源策略:

官方解释:同源策略是一个重要的安全策略,它用于限制一个origin的文档或者它加载的脚本如何能与另一个源的资源进行交互。它能帮助阻隔恶意文档,减少可能被攻击的媒介。

描述:例如:https://www.baidu.com https是协议,com为顶级域名,baidu是一级域名,www是主机名,必须协议(https)、域名(www.baidu.com)、端口号(8000)一样才能相互访问。

解决:

1.jsonp

定义:Jsonp(JSON with Padding) 是 json 的一种"使用模式",可以让网页从别的域名(网站)那获取资料,即跨域读取数据,其实就是前端定义一个回调函数告诉后端这个函数,请求后端时候后端执行这个函数,返回数据放在函数参数里面带回给前端。

实现如下:(端口号不一样实现的跨域)

node.js 服务器端的代码:

const express = require('express')
const app = express()
const port = 3000

app.get('/', (req, res) => {
    let query = req.query
    let cb  =query.cb
  res.send(`${cb}("我是服务器返回来的数据")`)
})

app.listen(port, () => {
  console.log(`Example app listening at http://localhost:${port}`)
})

客户端代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <h1>jsonp</h1>
    <script>
       function cb(data){
        console.log(data)//我是服务器返回来的数据
} </script> <script src="http://127.0.0.1:3000/?cb=cb"></script> </body> </html>

优缺点:只能发送get请求,不支持post,put,delete;不安全xss攻击,一般不采用

 

2.cors

定义:CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing),它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制,可以参考阮老师的仔细讲解(http://www.ruanyifeng.com/blog/2016/04/cors.html)

实现如下:

服务端代码:

const express = require('express')
const app = express()
const port = 4000
let whitList = ["http://localhost:3000"]
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")
        //设置允许的请求方法,默认get和post不需要配置
        res.setHeader("Access-Control-Allow-Methods","PUT")
        //预检不处理
        if(res.method=="OPTIONS"){
            res.end()
        }
        //预检检测时间(最大存活时间)
        res.setHeader("Access-Control-Allow-Max-Age",6)
        //设置允许带上cookie
        res.setHeader("Access-Control-Allow-Credentials",true)
        //设置允许获取的响应头参数
        res.setHeader("Access-Control-Expose-Headers",'age')
    }
    next()
})
app.get('/getData', (req, res) => {
    res.send(`get请求,我是服务器4000返回来的数据`)
})
app.put('/getData', (req, res) => {
    res.setHeader("age",20)
    res.send(`put请求,我是服务器4000返回来的数据`)
})
app.listen(port, () => {
    console.log(`Example app listening at http://localhost:${port}`)
})

前端代码:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
  <h1>cors</h1>
  <script>
      let xhr =  new XMLHttpRequest;
      xhr.open("PUT","http://localhost:4000/getData",true),
      //设置请求头
      xhr.setRequestHeader("name","jituitaixiao")
      //设置cookie
      document.cookie = 'name=jitiu'
      //强制带上cookie,默认不带上
      xhr.withCredentials = 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("age"))
            }
          }
      }
      xhr.send()

  </script>
</body>
</html>

优缺点:相比jsonp功能更加强大和安全,但是后端代码设置头部比较麻烦。

 

3.postMessage

定义:

postMessage是html5引入的API,postMessage()方法允许来自不同源的脚本采用异步方式进行有效的通信,可以实现跨文本文档,多窗口,跨域消息传递.多用于窗口间数据通信,这也使它成为跨域通信的一种有效的解决方案.
a.html页面,用node服务器启动本地到3000端口:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
   <h1>a.html</h1>
   <iframe src="http://localhost:4000/b.html" frameborder="0" id="iframe" onload="load()"></iframe>
    <script>
        //像b页面发送数据
        function load(){
            let iframe = document.getElementById("iframe");
            iframe.contentWindow.postMessage("a页面数据","http://localhost:4000")
            // 接收b页面数据
            window.onmessage = function(e){
                console.log(e.data)
            }
        }
    </script>
</body>
</html>

b.html页面,用node服务器启动本地到4000端口:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <h1>b.html</h1>
    <script>
        //接收b页面数据
        window.onmessage = function(e){
            console.log(e.data)
        //像a页面发送数据
            e.source.postMessage("b页面数据",e.origin)
        }
    </script>
</body>
</html>

把b页面通过iframe标签放在a页面,通过postMessage和onmessage相互通讯。

优缺点:html5引入的message的API可以更方便、有效、安全的解决这些难题。postMessage()方法允许来自不同源的脚本采用异步方式进行有限的通信,可以实现跨文本档、多窗口、跨域消息传递。

 

4.window.name

定义:下面代码有3个html页面,a页面和b页面同域,c页面独立,第一步,使用a页面的iframe标签的src属性指向c页面地址,此时不做处理就是跨域请求,会报错;第二步,在a页面的iframe标签上面定义一个onload方法,他的作用是将src的地址改成b页面地址,a和b页面属于同源,可以进行通信。开始在a页面请求c的时候,c页面抛出一个window.name,这个方法在后来a页面访问b的时候仍然存在,不会消失!这样a页面就拿到了c页面数据,实现了跨域!代码如下:

a.html(在本地服务的3000端口)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <iframe src="http://localhost:4000/c.html" frameborder="0" id="iframe" onload="onload"></iframe>
    <script>
        let first = true;//这个变量设置作用是修改src后页面从新渲染,会从新执行onload函数,防止死循环
        function onload(){
            if(first){
                let iframe = document.getElementById("iframe")
                iframe.scr = "http://localhost:3000/b.html"
                first = false
            }else{
                console.log(iframe.contentWindow.name)//"鸡腿太小"
            }
        }
    </script>
</body>
</html>

b.html(在本地服务的3000端口,不做任何处理)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    
</body>
</html>

c.html(在本地服务的4000端口)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <script>
        window.name = "鸡腿太小"
    </script>
</body>
</html>

优缺点:用的很少,有点low。。。

 

 

5.hash

定义:和window.name一样,三个页面a.html、b.html、c.html ,a页面和b页面同域,c为独立的域名,a页面通过iframe的src带一个hash值传递给c页面,在c页面通过获取hash,然后创建一个iframe,src待hash传给b页面,a页面可以通过监听b页面hash变化获取c的hash值

a.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <!-- a页面传递过一个hash值给c页面 -->
    <iframe src="http://localhost:4000/c.html#jituitaixiao" frameborder="0" id="iframe" onload="onload"></iframe>
    <script>
        // b页面修改了hash值,a页面监听获取hash值
     window.onhashchange = function (){
         console.log(location.hash)//jituizhendetaixioale
     }
    </script>
</body>
</html>

 b.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <script>
        //将c页面传递过来得hash值传给3000端口
        window.parent.parent.location.hash=location.hash
    </script>
    
</body>
</html>

 c.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <script>
        //获取a页面传递过来得hash值
        console.log(location.hash)//jituitaixioa
        //通过iframe向b页面传递hash
        let iframe = document.createElement("iframe");
        iframe.src = "http://localhost:3000/b.html#jituizhendetaixiaole"
        document.body.appendChild(iframe)
    </script>
</body>
</html>

  

6.document.domain

定义:首先用document.domain来指定域,是可以的,但是有局限性,也就是一级域名一致才可以。如下:

www.sojson.com  下指到sojson.com 是可以的。

icp.sojson.com  下指到 sojson.com 是可以的。

像上面是可以的,因为 一级域名  都是 sojson.com 。

www.sojson.com  下指到 www.baidu.com  是不行的。

sojson.com  指到 baidu.com  还是不行的。

比如我们要在当前页面下,“www.sojson.com/shiro” 下上传图片到 "cdn.sojson.com/images/" 下去。直接搞肯定是不行的。

在请求“www.sojson.com/shiro”和 "cdn.sojson.com/images/"的时候,都加上如下代码:

document.domain = "sojson.com"

  

7.webSocket

定义:它的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送技术的一种。

其他特点包括:

(1)建立在 TCP 协议之上,服务器端的实现比较容易。

(2)与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。

(3)数据格式比较轻量,性能开销小,通信高效。

(4)可以发送文本,也可以发送二进制数据。

(5)没有同源限制,客户端可以与任意服务器通信。

(6)协议标识符是ws(如果加密,则为wss),服务器网址就是 URL。

客户端代码如下:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
 <h1>WebSocket</h1>
<body>
    <script>

        // 创建一个3000长链接端口
        let socket = new WebSocket("ws://localhost:3000")
        //打开服务器,发送请求数据
        socket.onopen = function () {
            socket.send("jituitaixioa")
        }
        //接收webSocket返回来的数据
        socket.onmessage = function (evt) {
            console.log("Received Message: " + evt.data);
            socket.close();
        };
        //关闭长链接后的回调函数
        socket.onclose = function (evt) {
            console.log("Connection closed.");
        }
        //连接错误时候的回调函数
        socket.onerror = function (event) {
            console.log("Connection error");
            
        };

    </script>
</body>

</html>

 服务端代码如下:

let WebSocket = require("ws");
let wss = new WebSocket.Server({port:3000});
wss.on("connection",function(ws){
    ws.on("message",function(data){
        console.log(data)
        ws.send("jituzhendetaixiaole")
    })
})

 

 

8.nginx后端配置即可

9.proxy反向代理

感谢各位支持

 

posted @ 2021-04-07 17:14  鸡腿太小  阅读(1807)  评论(0编辑  收藏  举报