跨域常见的几种解决方案

深藏阁楼爱情的钟 2022-10-16 00:51 305阅读 0赞

跨域

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

  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 的一个简单案例

封装 JSONP

  • 每次像简单案例那样调用太过麻烦,现在我们想像 Axios 那样调用 jsonp

  • 实现代码如下:

    当前还可以实现 Promise 版本,这里就不再实现了

    可以参考:面试中如何实现一个高质量的JSONP

    ;(function () {
    const jsonp = function jsonp(config) {

    1. config == null ? (config = { }) : null
    2. typeof config !== 'object' ? (config = { }) : null
    3. let { url, params = { }, jsonpName = 'callback', success = Function.prototype } = config
    4. // 自己创建一个全局函数
    5. let f_name = `jsonp${ +new Date()}`
    6. window[f_name] = function (result) {
    7. typeof success === 'function' ? success(result) : null
    8. delete window[f_name]
    9. document.body.removeChild(script)
    10. }
    11. // 处理URL
    12. params = Qs.stringify(params)
    13. if (params) url += `${ url.includes('?') ? '&' : '?'}${ params}`
    14. url += `${ url.includes('?') ? '&' : '?'}${ jsonpName}=${ f_name}`
    15. // 发送请求
    16. let script = document.createElement('script')
    17. script.src = url
    18. // script.onerror = () => {};
    19. document.body.appendChild(script)

    }
    if (typeof window !== ‘undefined’) {

    1. window.jsonp = jsonp

    }
    })()

其它网站 JSONP 案例

  1. <script>
  2. jsonp({
  3. url: 'https://www.baidu.com/sugrec',
  4. params: {
  5. prod: 'pc',
  6. wd: '百度',
  7. },
  8. jsonpName: 'cb',
  9. success: result => {
  10. console.log(result)
  11. },
  12. })
  13. </script>

CORS 跨域资源共享

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

服务器代码

Allow-Origin 可以设置的值

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

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

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

客户端代码

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

Proxy 跨域代理

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

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

使用 webpack devServer 插件

  1. module.exports = {
  2. devServer: {
  3. port: '3000',
  4. compress: true,
  5. open: true,
  6. hot: true,
  7. proxy: {
  8. '/': {
  9. target: 'https://www.jianshu.com',
  10. changeOrigin: true,
  11. },
  12. },
  13. },
  14. }

通过代理爬取简书

  • 通过 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(‘./‘))

  • 前端代码

发表评论

表情:
评论列表 (有 0 条评论,305人围观)

还没有评论,来说两句吧...

相关阅读

    相关 常见解决方案

    常见的跨域解决方案: 跨域问题可以分为两种情况:前端跨域和后端跨域。以下是针对这两种情况的跨域解决方案: 前端跨域解决方案: 1. JSONP: 适用于前端向不同域

    相关 9解决方案

    什么是跨域 说起跨域,就要知道什么是浏览器同源策略 浏览器同源策略:必须是协议、域名、端口完全一致的才符合同源策略 如果以上三项,有一项不同都涉及到跨域问题 ---