浅析跨域原理及常用的跨域解决方案

什么是跨域

在前端开发中,我们经常会遇到 跨域 问题,那么,什么是 跨域 呢?用大白话来讲,跨域 就是我们访问了别人的资源。比如说我们通过 img link script 或者 接口请求 等方式来访问别人的资源,都是属于 跨域

当碰见 跨域 时,浏览器会有固定格式的报错信息,如下

Access to XMLHttpRequest at 'xxx' from origin 'yyy' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

我们会发现,通过 img linl script 标签来请求别的资源时,好像都没有相关的 跨域 报错信息(有些资源会做跨域拦截,禁止访问),那是因为浏览器对这些由 标签元素 跨域请求限制不大,基本都是直接放行的。

那么,为啥会有 跨域 问题呢,以及是如何产生的呢?

产生跨域的原因

产生跨域的原因主要是来在 浏览器的同源策略, 此处有两个重点 浏览器同源

跨域 发生的位置是在 浏览器,而非 服务器。不同服务器之间请求资源是不存在跨域问题的。

浏览器为了保护用户的安全,使用 同源策略 阻止一些资源的解析和执行。

同源策略是一个重要的安全策略,它用于限制一个源的文档或者它加载的脚本如何能与另一个源的资源进行交互。

它能帮助阻隔恶意文档,减少可能被攻击的媒介。例如,它可以防止互联网上的恶意网站在浏览器中运行 JS 脚本,从第三方网络邮件服务(用户已登录)或公司内网(因没有公共 IP 地址而受到保护,不会被攻击者直接访问)读取数据,并将这些数据转发给攻击者。

什么是同源策略

我们先来看下一个域名的组成吧

同源指的是: 协议 域名 端口号 必须一致

源1 源2 是否同源
a.com:80/b a.com:80/b 否 【协议不同】
a.com:80/b c.com:80/b 否 【域名不同】
a.com:80/b a.com:81/b 否 【端口不同】
a.com:80/b a.com:80/b/c

需要注意的一点,发生 跨域 时,浏览器会发起具体的请求,只是会对 跨域 请求进行报错提示,我们一起来看下具体的请求过程吧,如下图

跨域解决方案

1.CORS

CORS是 Cross Origin Resource Sharing 的缩写,翻译过来是 跨来源资源共享

CORS式一套机制,用于浏览器校验跨域请求。它的基本原理是:

只要服务器明确表示允许,则校验通过,服务器明确拒绝或没有表示,则校验不通过

这个也就决定了跨域目标服务器必须是自己的服务器

浏览器将CORS请求分为 简单请求预检请求(也叫非简单请求)

简单请求

简单请求 需要满足以下两个条件

  1. 请求方法为: HEAD GET POST
  2. HTTP的头信息不超出以下字段
  • Accept
  • Accept-Language
  • Content-Language
  • Last-Event-ID
  • Content-type: 只限于三个值: text/plain multipart/form-data application/x-www-form-urlencoded

请求过程

预检请求

不满足 简单请求 的都会转为 预检请求

请求过程

2.JSONP

JSONP是 JSON with Padding 的缩写

JSONP 是利用浏览器对标签的请求跨域限制较小的原理,实现的跨域

基本过程

jsonp整体工作流程(把jsonp封装到按钮点击事件里面)

  1. 浏览器: 声明函数接收服务器响应数据
  2. 浏览器: 点击按钮给页面添加script,添加额外参数callback=函数名
  3. 服务器 : 响应函数调用js代码 res.send('函数名(响应数据)')
  4. 浏览器: script标签会执行服务器响应的js
javascript 复制代码
// 前端

function CallBack(res) {
  console.log(res)
}

function JSONP_QUERY(url) {
  const script = document.createElement("script");
  script.src = url;
  script.onload = function() {
    script.remove()
  }
  document.body.appentChild(script);
}

document.querySelector("button").onclick = function() {
  JSONP_QUERY("http://www.abc.com?callback=CallBack");
}


// 后端
app.get("/abc", (req, res) => {
  const callback = req.query.callback;
  res.send(`${callback}(${data})`)
})

3.代理

以上两个方案,有个致命的缺点,就是服务器必须是自己的,所有跨域的的接口请求必须自己来处理,除非让别人服务器去兼容跨域处理,显然是不可行的,碰到这种情况,那我们就需要通过代理服务器来处理,它的基本原理就是利用服务器之间的请求是不在跨域,允许直接请求。

基本过程

相关推荐
小月土星3 小时前
React Router 前端路由深度解析:从传统后端路由到现代 SPA 的完整演进
前端
程序员黑豆3 小时前
鸿蒙开发 Navigation 路由教程:从入门到实战
前端·harmonyos
阿坤带你走近大数据4 小时前
基金面试准备1
面试·职场和发展
烬羽5 小时前
React Router v7 完全指南:一个 Demo 吃透前端路由
javascript·react.js·前端框架
烬羽5 小时前
React Router 嵌套路由:一个 `Outlet` 引发的布局思考
react.js·面试·前端框架
烬羽5 小时前
从"整个页面刷新"到"丝滑切换":手写一个 HashRouter 彻底搞懂前端路由
javascript·前端框架·全栈
用户938515635075 小时前
从零理解 React Router v6:每一个 API 都是怎么工作的
前端·javascript·全栈
用户059540174467 小时前
Redis缓存回滚踩坑实录:一个配置失误,让我在凌晨3点丢了3000条数据
前端·css
kyriewen8 小时前
别再这样写条件渲染了——你的React组件里藏着这5种定时炸弹
前端·javascript·react.js
IT_陈寒8 小时前
Redis缓存击穿把我坑惨了,原来这样设过期时间才靠谱
前端·人工智能·后端