JS - 这些跨域方法,你都知道吗?

678 阅读2分钟

跨域

协议、域名、端口不同,需要跨域

跨域解决方案

jsonp:JSON with Padding

  • <script> 标签没有同域限制
  • 服务端返回页面上 callback 包裹的 json 数据
  • 兼容性好
  • 仅支持 GET

CORS:Cross-origin resource sharing 跨域资源共享

简单请求 :

满足以下 2 条件

  • GET HEAD POST
  • Content-Type
    • application/x-www-form-urlencoded
    • multipart/form-data
    • text/plain

复杂请求:

不满足以上2条件

  • 需发送 OPTION 预检请求,查询服务器支持的方法

  • 按请求类别处理

      const express = require('express')
      const app = express()
      const whiteList = ['http://localhost']
      app.use((req, res, next) => {
        const origin = req.headers.origin
        if (whiteList.includes(origin)) {
          // 允许访问源,附带凭证请求时,必须指定 URI。相反,可设置为 *
          res.setHeader('Access-Control-Allow-Origin', origin)
          // 允许请求头,逗号隔开
          res.setHeader('Access-Control-Allow-Headers', 'authorization, username')
          // 允许请求方法,逗号隔开
          res.setHeader('Access-Control-Allow-Methods''GET, HEAD, POST, OPTIONS')
          /* 允许凭证
             客户端需配置 const xhr = new XMLHttpRequest()
             xhr.withCredentials = true */
          res.setHeader('Access-Control-Allow-Credentials', true)
          // 预检请求的返回结果,允许请求头和请求方法可以被缓存多久,缓存期间不再发送预检请求,单位秒
          res.setHeader('Access-Control-Allow-Max-Age', 10)
          // 允许响应头
          res.setHeader('Access-Control-Expose-Headers', 'authorization, username')
          if (req.method === 'OPTIONS') {
            res.end() // 预检请求直接返回空响应体
          }
        }
      })
    

postMessage

  • window属性

  • 跨窗口,跨框架frame、iframe,跨域

  • 允许不同源脚本采用异步方式进行有限通信

      otherWindow.postMessage(message, targetOrigin, [transfer])
      // otherWindow:
      iframe.contentWindow 属性
      window.open 返回
      window.frames 访问
      window.onmessage = e => console.log(e.data) // 接收返回数据
    

Websocket

  • 基于 TCP

  • 全双工通信

  • 应用层协议

      // 客户端
      const socket = new WebSocket('ws://localhost:3000')
      socket.onopen = () => socket.send('message') // 向服务器发送数据
      soket.onmessage = e => console.log(e.data) // 接收服务器返回数据
      // server.js
      const express = require('express')
      const app = express()
      const ws = require('ws')
      const wss = new ws.Server({port: 3000})
      wss.on('connection', ws => {
        ws.on('message', data => console.log(data))
      })
    

代理服务器转发

  • 代理服务器设置 CORS 请求头字段

    • nginx
      • add_header
    • kangle
      • 回应控制,增加表,标记模块,add_header
    • nodejs
      • response.writeHead
  • 代理服务器向源服务器继续请求

    • nginx
      • 七层 HTTP 代理:配置 http

        http {
            server {
              listen 80;
              location / {
                proxy_pass http://127.0.0.1:3000
              }
            }
        }
        
      • 四层 TCP 代理:配置 stream

          steam {
            upstream proxyServer {
              server 127.0.0.1:3000;
            }
            server {
              listen 80;
              proxy_pass proxyServer;
            }
          }
        
    • kangle
      • 七层 HTTP 代理
        • 请求控制,增加表
        • 目标设置为代理
        • 匹配模块设置原域名和端口,标记模块设置目标域名和端口
    • nodejs
      • 七层 HTTP 代理

    const http = require('http') http.request({ host: '127.0.0.1', port: 3000, url: '/' })

document.domain

  • 主域名相同,子域名不同
  • 设置值为 主域名

iframe

  • window.name
    • 不超过 2MB
    • 同一个iframe
      • 加载跨域页,跨域页设置 window.name
      • 加载同域页,读取 iframe.contentWindow.name
  • location.hash
    • 不会被 URL 编码
    • 加载跨域页,参数跟在 # 后。window.onhashchange 监听 hash 变化
    • 跨域页获取 location.hash,加载同域页,参数跟在 # 后
    • 同域页设置 window.parent.parent.location.hash = location.hash,将参数回传