跨域的几种解决方法

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

什么是跨域?

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

浏览器的同源策略

跨域是因为浏览器的同源策略。浏览器安全的基石是"同源政策",所谓"同源"指的是"三个相同":协议 - 域名 - 端口 都相同才算同源,比如说有个地址 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详解

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

相关推荐
PaytonD几秒前
LoopBack 2 如何设置静态资源缓存时间
前端·javascript·node.js
snow@li4 分钟前
d3.js:学习积累
开发语言·前端·javascript
编程岁月9 分钟前
java面试-0203-java集合并发修改异常、快速/安全失败原理、解决方法?
java·开发语言·面试
一枚前端小能手26 分钟前
🚀 巨型列表渲染卡顿?这几个优化技巧让你的页面丝滑如德芙
前端·javascript
酷柚易汛智推官26 分钟前
Electron技术深度解析:跨平台桌面开发的利器与挑战
前端·javascript·electron
llz_11232 分钟前
第五周作业(JavaScript)
开发语言·前端·javascript
W.Y.B.G1 小时前
JavaScript 计算闰年方法
开发语言·前端·javascript
渣哥1 小时前
你以为只是名字不同?Spring 三大注解的真正差别曝光
javascript·后端·面试
小六路1 小时前
可以横跨时间轴,分类显示的事件
前端·javascript·vue.js
SuperherRo1 小时前
JS逆向-安全辅助项目&JSRpc远程调用&Burp插件autoDecode&浏览器拓展V_Jstools(上)
javascript·安全·项目