跨域常见的几种解决方案

跨域

非同源策略请求(比较协议、域名、端口号,只要有一个不一样就是跨域):

  1. 页面的访问地址(Web地址)
  2. 数据接口的请求地址

情况1:开发时候是跨域的,但是服务器部署的时候是同源的

  • 修改本地 HOST【DNS解析】

    核心:骗过浏览器,让浏览器认为是同源,但是本质还是跨域

情况2:开发和上线都是跨域的

  • JSONP(不安全,并且只支持GET请求)

  • 其它方案

    document.domain + iframe

    window.name + iframe

    H5 postMessage

  • CORS 跨域资源共享

  • proxy 跨域代理(目前最常用的)

JSONP 跨域资源请求

利用<script> 或者<link> <img> <iframe>... 不存在域的限制

  • 特征:资源访问一定都是GET请求,不可能有POST

全局函数 function fn(result) { ... }

  1. <script src='http://127.0.0.1:8888/user/list?callback=fn'> 一定能发送到服务器(不存在域的限制),把全局函数 fn 名字,当做参数传递给服务器
  2. 服务器接收到这个请求,同时也可以获取 callback 传递的值(fn)
  3. 准备数据,最后返回给客户端 fn([10,20,30])
  4. 客户端把函数 fn 执行,把服务器准备的数据作为实参传递给函数的形参

服务器代码

  • 首先看一下服务器代码,这里提供了 jsonpTest 这个资源路径,并返回给客户端 一个字符串(包含执行函数和参数)
const express = require('express')
const bodyParser = require('body-parser')
const app = express()

const port = 1001
app.listen(port, () => {
    
    
  console.log(`The Web Service Is Listening To The Port: ${
      
      port}`)
})

app.get('/jsonpTest', (req, res) => {
    
    
  let fname = req.query.callback
  let data = [10, 20, 30]
  res.send(`${
      
      fname}(${
      
      JSON.stringify(data)})`)
})

JSONP 简单案例

  • 现在来尝试一下 jsonp 的一个简单案例
<script>
  function func(result) {
      
      
    console.log(result)
  }
</script>
<script src="http://127.0.0.1:1001/jsonpTest?callback=func"></script>

封装 JSONP

  • 每次像简单案例那样调用太过麻烦,现在我们想像 Axios 那样调用 jsonp
<script src="node_modules/qs/dist/qs.js"></script>
<script src="1.js"></script>
<script>
  jsonp({
      
      
    url: 'https://www.baidu.com/sugrec',
    params: {
      
      
      prod: 0,
      from: 'qq',
    },
    jsonpName: 'callback',
    success: result => {
      
      
      console.log(result)
    },
  })
</script>
;(function () {
    
    
  const jsonp = function jsonp(config) {
    
    
    config == null ? (config = {
    
    }) : null
    typeof config !== 'object' ? (config = {
    
    }) : null
    let {
    
     url, params = {
    
    }, jsonpName = 'callback', success = Function.prototype } = config

    // 自己创建一个全局函数
    let f_name = `jsonp${
      
      +new Date()}`
    window[f_name] = function (result) {
    
    
      typeof success === 'function' ? success(result) : null
      delete window[f_name]
      document.body.removeChild(script)
    }

    // 处理URL
    params = Qs.stringify(params)
    if (params) url += `${
      
      url.includes('?') ? '&' : '?'}${
      
      params}`
    url += `${
      
      url.includes('?') ? '&' : '?'}${
      
      jsonpName}=${
      
      f_name}`

    // 发送请求
    let script = document.createElement('script')
    script.src = url
    // script.onerror = () => {};
    document.body.appendChild(script)
  }
  if (typeof window !== 'undefined') {
    
    
    window.jsonp = jsonp
  }
})()

其它网站 JSONP 案例

<script>
  jsonp({
    
    
    url: 'https://www.baidu.com/sugrec',
    params: {
    
    
      prod: 'pc',
      wd: '百度',
    },
    jsonpName: 'cb',
    success: result => {
    
    
      console.log(result)
    },
  })
</script>

CORS 跨域资源共享

在发送真实请求之前,浏览器会先发送一个试探性请求 OPTIONS(目的:测试客户端和服务器之间是否可以正常通信)如果可以正常通信,接下来再发送真实请求信息

服务器代码

Allow-Origin 可以设置的值

  • 单一源
  • * 所有源(但是此时不安全,而且不允许携带资源凭证)

假如你希望有多个源(不是所有源)都可以跨域,这时就需要设置一个白名单

// 白名单
const safeList = [, 'http://127.0.0.1:5500', 'http://127.0.0.1:5501']
app.use((req, res, next) => {
    
    
  let origin = req.headers.origin || req.headers.referer
  origin = origin.replace(/\/$/, '')
  if (safeList.includes(origin)) {
    
    
    res.header('Access-Control-Allow-Origin', origin)
    res.header('Access-Control-Allow-Credentials', true)
    req.method === 'OPTIONS' ? res.send('Current Services Support Domain Request!') : next()
  }
})
app.get('/test', (req, res) => {
    
    
  res.send('OK')
})

客户端代码

<script>
  fetch('http://127.0.0.1:1001/test').then(response => response.text()).then(data => console.log(data))
</script>

Proxy 跨域代理

爬虫:自己写一个后台,去爬取别的后台的数据(平台和平台之间没有跨域)

  • 后台和后台之间没有跨域限制(服务器一般会做白名单)
  • 客户端和服务器才有跨域限制(浏览器的安全性)

使用 webpack devServer 插件

module.exports = {
    
    
  devServer: {
    
    
    port: '3000',
    compress: true,
    open: true,
    hot: true,
    proxy: {
    
    
      '/': {
    
    
        target: 'https://www.jianshu.com',
        changeOrigin: true,
      },
    },
  },
}

通过代理爬取简书

  • 通过 pipe 实现请求代理
const request = require('request')app.get('/subscriptions/recommended_collections', (req, res) => {
    
      let url = 'https://www.jianshu.com/asimov' + req.url  req.pipe(request(url)).pipe(res)})// 注意:这里html文件名必须为index.htmlapp.use(express.static('./'))
  • 前端代码
<script>  fetch('/subscriptions/recommended_collections').then(response => response.text()).then(data => console.log(    data))</script>

猜你喜欢

转载自blog.csdn.net/qq_38689395/article/details/117280656