跨域的几种解决方法

本文参考阮一峰老师的 浏览器同源政策及其规避方法

什么是跨域?

在实际的开发工作中,我们经常会有跨域请求服务器数据的情况,经常碰到跨域问题需要处理,那么什么是跨域呢?接下来我们好好聊一聊。

浏览器的同源策略

跨域是因为浏览器的同源策略。浏览器安全的基石是"同源政策",所谓"同源"指的是"三个相同":协议 - 域名 - 端口 都相同才算同源,比如说有个地址 clerverSnnail.cn:3000 ;https就是协议号,://clerverSnnail就是域名,:3000端口号,其中有一个不相同那就是跨域

这里我们先做个简单的demo领略一下浏览器的同源策略,首先前端代码和后端代码都创建一份

client文件夹里的index.html写前端,引入jquery,使用ajax方法朝http://localhost:3000 这个地址发接口请求获取数据,代码如下:

js 复制代码
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.7.1/jquery.min.js"></script>

<body>
  <button id="btn">获取数据</button>

  <script>
    let btn = document.getElementById('btn');
    btn.addEventListener('click', () => {
      $.ajax({
        url: 'http://localhost:3000',
        method: 'get', //请求的方式get
        data:{  //向后端传递的参数
          name: '二蛋'
        },
        success(res){
          console.log(res); //请求到东西便会打印
        }
      })
    })
  </script>
</body>

server文件夹,使用终端打开,输入 npm init -y 初始化文件夹,npm i koa 安装koa,借助node框架koa在index.js中写后端,代码如下:

js 复制代码
const Koa = require('koa');
const app = new Koa()

const main = (ctx) => {
  ctx.body = 'hello world'  //向前端输出的内容
}

app.use(main)

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

运行前后端代码,当我们点击获取数据按钮,前端就会朝http://localhost:3000 发接口请求获取数据,那么这时候我们能拿到后端给的'hello word'吗?让我们看看

好的报了个经典错误,浏览器的同源策略不允许我在://loacalhost:5500的域名去请求://loacalhost:3000的数据,这就出现了跨域问题。

那么我们怎么解决这个问题呢?接下来我们来聊一聊

跨域的解决方案:

1. JSONP

jsonp(JSON with Padding),是JSON的一种 "使用模式",可以让网页跨域读取数据,其本质是利用script标签的开放策略。

(1)前端创建一个srcipt标签, 借助该标签的src属性朝后端发送请求,且前端在window上声明一个函数(callback),并将该函数名拼接在src属性的路径后面作为参数传递给后端

(2)后端接收到前端的请求且获取到前端传递过来的函数名,将需要响应给前端的数据拼接在该函数的调用中(相当于作为实参传递)

(3)前端接收到后端的响应,相当于在执行window上声明的函数(callback),遂该函数的参数就是后端响应回来的数据

缺点

(1)需要后端配合

(2)只适用于GET请求(因为浏览器加载script的src属性默认就是GET请求,无法修改)

下面我们用代码实现一下,前端在上面dome的基础上index.html中做个简单的封装,使用Promise包裹一层,我希望在jsonp函数调用的时候后面能接.then

js 复制代码
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js"></script>

<body>
  <button id="btn">获取数据</button>

  <script>
    function jsonp(url, cb) {
      return new Promise((resolve, reject) => {
        const script = document.createElement('script') //创建script标签
        script.src = `${url}?cb=${cb}` // http://localhost:3000?cb=xxx
        document.body.appendChild(script)
        // 拿到了后端返回的一个函数
        window[cb] = (data) => {
          // 操作后端携带过来的数据
          resolve(data)
        }
      })
    }
    //当我们点击获取数据按钮的时候,就会调用jsonp(),朝地址发起请求
    let btn = document.getElementById('btn')
    btn.addEventListener('click', () => {
      jsonp('http://localhost:3000', 'callback').then(res => {
        console.log(res);
      })
    })
  </script>
</body>

那么前端借助script标签请求回来的数据怎么拿到页面上用呢?这时候就需要后端配合了

js 复制代码
const Koa = require('koa')
const app = new Koa()
//假设data是需要给前端的数据
const data = {
  name: '二蛋',
  age: 18
}

const main = (ctx) => {
  const { cb } = ctx.query
  // 创建一个字符串,就是callback函数的调用 
  const str = `${cb}(${JSON.stringify(data)})`  //'callback({name: '二蛋',age: 18})'
  
  ctx.body = str //浏览器读到这个字符串会直接读成代码的格式
}
app.use(main)

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

运行起来,点击按钮,这样就可以不触发同源策略拿到后端给的数据了

2. CORS

跨域资源共享(Cross-origin resource sharing),相比JSONP只能发GET请求,CORS允许任何类型的请求,通过设置响应头,告诉浏览器不需要走同源策略的保护机制。

回到demo中的代码,回到最初的样子,前端代码不变,后端在server文件夹打开终端,npm i @koa/cors 安装cors

js 复制代码
const Koa = require('koa');
const app = new Koa()
const cors = require('@koa/cors') //引入安装好的cors

app.use(cors()) //

const main = (ctx) => {
  ctx.body = 'hello world' //向前端输出的内容
}

app.use(main)

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

运行前后端,点击获取数据按钮,这样我们就直接获取到了后端传过来的数据

哇这是不是简直不要太简单,仅仅是增加了两行代码就解决了跨域问题,但是这个方法并不优雅,如果在线上环境就这样使用的话,那么是不是谁都可以访问这个数据了?所以这样只适合在开发环境下使用,但是也有弥补措施:

js 复制代码
app.use(cors({
   origin: 'http://127.0.0.1:5500' //设置只允许这个源访问
 }))

上面这个是借助别人写的插件来解决的,那么我们看看原生node怎么用cors,需要服务器配置Access-Control-Allow-Origin头信息,下面看看是如何实现的:

js 复制代码
const http = require('http')

const server = http.createServer((req, res) => {
  res.writeHead(200, {
    "Access-Control-Allow-Origin": '*' // 被允许的源   *号就是所有的源
  })
  res.end('hello cors')
})

server.listen(3000)

关于CORS更详细的介绍可参考阮一峰老师的 跨域资源共享CORS详解

方法先介绍到这里,后续方法还会持续更新...

相关推荐
围巾哥萧尘1 小时前
大型语言模型语境学习中的演示位置偏置(DPP Bias)研究🧣
面试
百思可瑞教育2 小时前
Vue中使用keep-alive实现页面前进刷新、后退缓存的完整方案
前端·javascript·vue.js·缓存·uni-app·北京百思可瑞教育
getdu2 小时前
Redis面试相关
数据库·redis·面试
bobz9652 小时前
libvirt 内存消耗
面试
木心操作3 小时前
js生成excel表格进阶版
开发语言·javascript·ecmascript
GISer_Jing3 小时前
sqb&ks二面(准备)
前端·javascript·面试
前端码虫3 小时前
2.9Vue创建项目(组件)的补充
javascript·vue.js·学习
夜宵饽饽3 小时前
上下文工程实践 - 工具管理(上篇)
javascript·后端
汤姆Tom3 小时前
JavaScript Proxy 对象详解与应用
前端·javascript