likes
comments
collection
share

(二)层层突破之跨域

作者站长头像
站长
· 阅读数 9

层层突破

上次我们聊完了回流重绘,今天聊聊跨域吧!

一、什么是跨域?

要知道什么是跨域,我们必须先知道浏览器的同源策略,同源策略的定义如下:

定义:协议号 - 域名 - 端口号相同才算同源

举个例子:http://(协议号) www.xxxx.cn (域名) :8080(端口号)

二、浏览器为什么要有同源策略?

我们这么想,如果你不小心在银行站点打开了一个恶意网站,在没有同源策略的情况下,他可以劫持到你的登录账号及密码,那后果你懂得。所以,总的来说是为了:

禁止第三方站点朝A网站请求数据,降低CSRF攻击的可能

三、跨域发生在什么时候?

后端响应回来的数据,在浏览器上被终止

四、解决跨域的方法

解决跨域的方法有很多种,这里介绍最常见的JSONP和cors

1.JSONP

jsonp 必须要后端的配合,且只适用于get请求,主要借助了html的script标签src属性不会触发同源策略的特性。前端在window上挂一个回调函数,通过script标签的src属性向后端发一个请求,把函数名作为参数一起传给后端,后端需要配合将函数的调用返回给前端,当前端拿到这个函数的调用会将它执行,那么后端需传参数时,统一作为函数的参数传递给前端即可。

前端代码如下:

<!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">
  <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js"></script>
  <title>Document</title>
</head>
<body>
  <button id="btn">获取数据</button>
  <script>
    function jsonp(url,cb){
      return new Promise((resolve,reject)=>{
        const script=document.createElement('script');
        script.src=`${url}?cb=${cb}`
        document.body.appendChild(script);
        //拿到了后端返回的一个函数
        window[cb]=(data)=>{  //往window上挂了一个函数cb,接受后端传过来的数据形参data
          //操作后端携带过来的数据
          resolve(data)
        }
      })
    }
    let btn=document.getElementById("btn");
    btn.addEventListener("click",()=>{
     jsonp('http://localhost:3000','callback').then(res=>{
      console.log(res);
     })
    })
  </script>
</body>
</html>

后端代码如下:

const Koa=require('koa')
const app=new Koa()

const data={
  name:'zt_ever',
  age:18
}

const main=(ctx)=>{
  const {cb}=ctx.query  //接受前端的数据cb
  const str=`${cb}(${JSON.stringify(data)})`  //'创建字符串,callback的调用,并传入数据
  ctx.body=str
}
app.use(main)

app.listen(3000,()=>{
  console.log('项目已启动');
})

2. cors

纯后端实现,通过响应头告诉浏览器哪些源不需要走同源策略

那么cors是怎么实现的呢?

const http =require('http')  

const server= http.createServer((req,res)=>{ // 创建一个后端服务
    //  通过响应头告诉浏览器哪些不需要跨域
    res.writeHead(200,{  // 响应头设置,成功就返回 200 的状态码
        "Access-Control-Allow-Origin":"*",  // 允许所有的源向我发请求(配置一个白名单)
        "Access-Control-Allow-Methods":"GET,POST,PUT,OPTIONS",// 允许请求的方式
        "Access-Control-Allow-Headers":"Content-Type", // 允许请求头设置类型
    })

    // 在向前端响应的数据
    res.end('hello cors')
})

server.listen(3000,()=>{
    console.log('cors项目已启动');
})

3. node代理

同源策略不发生在服务端,它是浏览器的策略,它限制的是客户端,我们可以自己创建一个可以跨域后端自己的后端可以向目标后端请求数据,让前端自己的后端请求数据。这样就通过自己的后端间接获取到了目标后端的数据。

前端代码如下:

<!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>
  <button id="btn">点我</button>

  <script>
    let btn = document.getElementById('btn')

    btn.addEventListener('click', () => {
      var ajax = new XMLHttpRequest()
      ajax.open('get', 'http://localhost:3001')
      ajax.send()
      ajax.onreadystatechange = () => {
        if (ajax.readyState == 4 && ajax.status == 200) {
          console.log(ajax.responseText);
        }
      }
    })
  </script>
</body>
</html>

后端代码如下:

const http = require('http');
const urllib = require('url');

http.createServer(function (req, res) {
  res.writeHead(200, {
    "Access-Control-Allow-Origin": "*" //允许所有源向我发请求
  })

  http.request({
    host: '192.168.43.107', // 目标后端
    port: 3000,
    method: 'GET'
  }, result => {
    result.on('data', function(msg) {
      res.end(msg.toString()) //向前端返回数据
    })
  }).end()


}).listen(3001, () => {
  console.log('listening on port 3001');
});

4. nginx

原理与node代理相同

5. postMessage

当页面中使用 iframe 嵌套了子页面,父子页面不同源时,postMessage跨域实现通信

6. domain

通过声明父子页面的 document.domain = 'xxx'来告诉浏览器无需跨域

7. WebSocket

socket协议不受同源策略限制