跨域之cors和postMessage

123 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第31天,点击查看活动详情

1、cors

直接在服务端做手脚

  • node直接显示页面
...
<body>
  HELLO
</body>
// server1.js
const express = require("express")
const app = express()
app.use(express.static(__dirname)) // 以当前目录作为静态文件目录
app.listen(3000)

启动服务后,访问 localhost:3000/index.html ,即可看到页面 HELLO

为了实现跨域,我们再启动一个 4000 端口的服务

// server2.js
const express = require("express")
const app = express()

app.get("/getData", function(req,res) {
  res.end("我是4000的getData")
})

app.use(express.static(__dirname)) // 以当前目录作为静态文件目录
app.listen(4000)
  • 修改 index.html
<body>
  <script>
    let xhr = new XMLHttpRequest()
    xhr.open("GET", "http://localhost:4000/getData", true)
    xhr.withCredentials = true; // 强制携带凭证:cookie默认是不允许跨域的,加上这段代码,就可以让跨域请求强制加上cookie
    xhr.setRequestHeader("name", "hayes") // 设置请求头(服务器也需要相应的允许设置)
    xhr.onreadystatechange = function() {
      if(xhr.readyState === 4) {
        if(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304) {
          console.log(xhr.response);
        }
      }
    }
    xhr.send()
  </script>
</body>

此时两个服务都启动起来,再访问 localhost:3000/index.html,会发现跨域了。

1.png

server2 中添加一行打印,来观察一下请求头

app.get("/getData", function(req,res) {
  console.log(req.headers); // 观察请求头
  res.end("我是4000的getData")
})

2.png

  • 有请求头打印,说明请求发过来了
  • 可以看到请求源是 localhost:3000 端口的

修改server2

const express = require("express")
const app = express()

const whiteList = ["http://localhost:3000"] // 允许跨域的白名单
app.use(function(req, res, next) {
  let origin = req.headers.origin; // 取出请求源
  if(whiteList.includes(origin)) {
    res.setHeader("Access-Control-Allow-Origin", origin) // 设置哪个源可以访问我
    res.setHeader("Access-Control-Allow-Headers", "name") // 允许的请求头
    res.setHeader("Access-Control-Allow-Methods", "PUT") // 允许PUT请求
    res.setHeader("Access.Control-Allow-Credentials", true) // 允许携带cookie
    res.setHeader("Access-Control-Max-Age", 6) // 预检存活时间:6秒内不会再次发送预检请求
    if(res.method === "OPTIONS") { // 预检请求不是每次都发
      res.end() // 预检请求不做任何处理
    } 
  }
  next()
})

app.get("/getData", function(req,res) {
  console.log(req.headers);
  res.end("我是4000的getData")
})

app.use(express.static(__dirname))
app.listen(4000)

此时再访问,就可以看到控制台打印的结果了:我是4000的getData

2、postMessage

两个页面之间通信

  • 页面a
<body>
  a
</body>
  • 页面b
<body>
  b
</body>
  • 服务a
const express = require("express")
const app = express()

app.use(express.static(__dirname)) // 以当前目录作为静态文件目录
app.listen(3000)
  • 服务b
const express = require("express")
const app = express()

app.use(express.static(__dirname)) // 以当前目录作为静态文件目录
app.listen(4000)
  • 现在我们想把 b 页面 嵌入 a 页面
// 页面a
<body>
  <iframe src="http://localhost:4000/b.html" frameborder="0" id="iframe" onload="load()"></iframe>
  <script>
    function load() {
      // 在加载完成(load)后,拿到b的iframe,像页面b(4000端口)发消息
      let iframe = document.getElementById("iframe");
      iframe.contentWindow.postMessage("你好呀", "http://localhost:4000")
    }
  </script>
</body>
  • 页面b接收消息
<body>
  <script>
    window.onmessage = function(e) {
      console.log(e.data); // 打印消息
    }
  </script>
</body>

3.png

打开页面 a,可以看到页面 b 打印的消息

  • 接下来我们让页面 b 回发消息给“消息源”(也就是页面a)
<body>
  <script>
    window.onmessage = function(e) {
      console.log(e.data);
      e.source.postMessage("我不好", e.origin) // 回发消息至哪个源
    }
  </script>
</body>
  • 页面a接收回发(postMessage)的消息
<body>
  <iframe src="http://localhost:4000/b.html" frameborder="0" id="iframe" onload="load()"></iframe>
  <script>
    function load() {
      let iframe = document.getElementById("iframe");
      iframe.contentWindow.postMessage("你好呀", "http://localhost:4000")
      // 接收自己的window的消息
      window.onmessage = function(e) {
        console.log(e.data);
      }
    }
  </script>
</body>

现在访问 http://localhost:3000/a.html就可以看到两条打印出的消息了

4.png

页面a 中嵌入页面b => a向b发送消息 => b接收到消息后,回发给a另外一条消息 => 页面a接收到b回发的消息