前端跨域问题解决
一、同源策略
同源策略是一个重要的安全策略,它用于限制一个Origin的文档或者它加载的脚本如何能与另一个源的资源进行交互。它能帮助阻隔恶意文档,减少可能被攻击的媒介。
Origin:指web文档的来源,Web 内容的来源取决于访问的URL的方案 (协议),主机 (域名) 和端口定义。只有当方案,主机和端口都匹配时,两个对象具有相同的起源。

二、跨域
关于URL是否同源,根据上图中的①②③进行判断即可,只要有一点不同,就达到跨域的条件。顺带一提,即便是向域名对应的ip进行资源请求,仍然会跨域。
IE的特殊性:Internet Explorer 的同源策略有两点差异,一是IE未将端口号纳入同源策略的检查,其次是两个高度互信的域名也不受同源策略的检查。
常见的跨域情景:
浏览器内常见的跨域报错:
![]()
跨域出现的场景:
一般常见于开发阶段,本地启动项目后,当前页面域名和后台服务器域名不相同,导致跨域。在项目上线后,会通过统一域名、后端配置域名白名单等方式避免跨域。
下方的解决方案中,我们通过koa2框架搭建服务器,实现一系列的情景模拟。
三、跨域的解决方案
1.JSONP
原理:通过script标签没有跨域限制的特性,进行资源的请求和获取。
限制:需要目标服务器进行配合,且仅支持get请求
我们直接通过代码和注释,理解jsonp的使用前端代码如下:
<script>
window.jsonp = function(res){
console.log(res);
}
</script>
<script src="http://localhost:9527/jsonp?val=123&cb=jsonp"></script>
后端代码如下:
// 定义jsonp接口
router.get('/jsonp', async (ctx, next) => {
/*
1.后端通过query获取前端传来的请求参数
其中包括:
· 交予后端进行功能逻辑操作的数据,如val
· 交予后端进行jsonp操作的函数名,如cb
*/
const {cb, val} = ctx.query
// 2.调用回调函数,进行传参,将处理好的数据返回给前端
if(val === '123'){
const requestData = {
code: 10001,
data: '登陆成功'
}
//在响应体中触发目标函数,并将处理好的数据requestData作为实参传入
ctx.body = `${cb}(${JSON.stringify(requestData)})`;
}
})
前端通过window对象,在全局挂载了一个待触发的函数。
后端通过响应体触发这个函数,并将数据作为入参,传给前端。
了解简单的实现后,前端可以对jsonp的功能再进行一层封装:
/*
1. 生成script标签,我们需要script标签进行接口的调用
2. 处理参数数据,分别整理好接口,接口参数,函数名等数据,并进行填充
3. 写入生成好的script标签,实现接口的调用(返回promise对象,便于链式调用)
4. 清除script标签
*/
function jsonp(requestData) {
// 对传入参数进行处理
const { url, data, jsonp } = requestData;
let query = '';
for (let key in data) {
query += `${key}=${data[key]}&`;
}
const src = `${url}?${query}jsonp=${jsonp}`;
// 生成、填充script标签,在页面中挂载调用接口
let scriptTag = document.createElement('script');
scriptTag.src = src;
document.body.appendChild(scriptTag);
return new Promise((resolve, reject) => {
window[jsonp] = function(rest){
resolve(rest)
document.body.removeChild(scriptTag)
}
})
}
// 整理数据
const requestData = {
url: 'http://localhost:9527/jsonp',
data: {
val: 123,
},
jsonp: 'getMessage'
}
// 接口调用
btn.onclick = function () {
jsonp(requestData).then(function (response) {
console.log(response);
})
}
2.CORS
Cross-Origin Resource sharing(跨域资源共享),是一种基于HTTP头的机制,该机制允许服务器标示除了它自己以外其他origin(域名,协议和端口),既浏览器在跨域的情景下仍然能从目标服务器请求并获取资源。
而对服务器数据可能产生副作用的HTTP请求方法,都会触发CORS中的预检机制。
CORS中通过预检机制(preflight request)检查服务器是否允许浏览器发送真实请求,浏览器会先发送一个预检请求(option请求),请求中会携带真实请求的请求信息:
origin:请求的来源
Access-Control-Request-Method:
通知服务器在真正的请求中会采用哪种HTTP方法(GET,POST,DELETE...)
Access-Control-Request-Headers:通知服务器在真正的请求中会采用哪些请求头
服务器可以在预检请求中,可以根据以上三条信息,确定预检请求是否通过:
//server.js
app.use(async (ctx, next) => {
// 允许跨域资源共享的白名单
const whiteList = ['http://127.0.0.1:5500']
// 判断目标源是否通行
const pass = whiteList.includes(ctx.header.origin)
// 对于预检请求,如果没有设置正确的响应状态,浏览器会直接拦截真实请求,直接报错提示跨域
// 所以我们可以在这一部分,确定客户端的请求是否符合我们的要求
if (ctx.method === "OPTIONS") {
if (!pass) return
// 预检放行
ctx.status = 204
}
await next();
});
响应的状态码是决定预检请求是否通过的关键,返回正常的状态码(通常是204)就能通过预检请求,让浏览器发出真实的请求。
在代码中也可以看出,pass是决定预检请求的关键,那在实际的项目中,还得根据设计去决定通行的具体条件。当通过预检请求后,后台可以设置对应的响应头数据,例如是否允许目标源跨域资源共享:
//server.js
app.use(async (ctx, next) => {
console.log('middleware for cors');
// 允许跨域资源共享的白名单
const whiteList = ['http://127.0.0.1:5500']
// 判断目标源是否通行
const pass = whiteList.includes(ctx.header.origin)
// 对于预检请求,如果没有设置正确的响应状态,浏览器会直接拦截真实请求,直接报错跨域
// 所以我们可以在这一部分,确定客户端的请求是否符合我们的要求
if (ctx.method === "OPTIONS") {
if (!pass) return
// 预检放行
ctx.status = 204
}
// 允许访问的origin
ctx.set("Access-Control-Allow-Origin", ctx.headers.origin);
// cookie是否允许携带
ctx.set("Access-Control-Allow-Credentials", true);
// 允许访问的HTTP方法
ctx.set("Access-Control-Request-Method", "PUT,POST,GET,DELETE,OPTIONS");
// 哪些请求头允许通行
ctx.set(
"Access-Control-Allow-Headers",
"X-Requested-With,Content-Type,Accept,Origin"
);
// 暴露给客户端的响应头信息,在不设置的情况下,客户端只能获取默认的响应头,如’content-type‘
ctx.set(
"Access-Control-Expose-Headers",
"With-Requested-Key"
);
// 设置对应的响应头数据
ctx.set(
"With-Requested-Key",
"HW"
);
// 预检结果的缓存时间,毫秒为单位,Firefox上限是86400-24小时,Chromium(谷歌引擎)上限是7200-2小时
ctx.set("Access-Control-Max-Age", 0);
await next();
});
其中需要注意两个点:
关于Access-Control-Expose-Header
使用CORS时,浏览器只允许获取默认的响应头,像上文代码中的标头With-Requested-Key,即便我们可以通过浏览器的调试器查看,也无法通过代码去获取,这时候就需要后台通过Access-Control-Expose-Header进行暴露(后台代码在已在上方统一贴出)。
前端代码
<body>
<button id="btn"> 请求资源 </button>
</body>
<script>
btn.onclick = function () {
axios.post('http://localhost:9527/getMessage', {
firstName: 'Fred',
lastName: 'Flintstone'
})
.then(function (response) {
// 可以在里面查找到暴露出来的响应头数据,如’With-Requested-Key‘: "HW"
console.log(response.headers);
})
.catch(function (error) {
console.log(error);
});
}
</script>
关于Access-Control-Allow-Credentials
使用CORS时,默认不携带cookie,需要同时满足三个条件,才能在使用CORS时进行cookie的传递:
浏览器的请求中,设置withCredentials参数为true
服务端设置标头Access-Control-Allow-Credentials为true
服务端设置标头Access-Control-Allow-Origin不为*
我们可以在原生ajax请求中设置该参数,或者在axios的默认配置中设置该参数:
// 原生ajax
const xhr = new XMLHttpRequest()
xhr.withCredentials = true
// axios
axios.defaults.withCredentials = true;
Ok,明白CORS的作用,以及明白CORS中的预检机制后,接下来是了解什么时机下会触发预检机制。
CORS中归纳了一系列不会触发预检机制的请求场景,即满足所有下述条件的情况下,统称为简单请求:
使用这三种方法之一:GET HEAD POST
不得人为设置此集合外的其他首部字段:Accept Accept-Language Content-Language Content-Type
Content-type的值仅限于这三者之一:
text/plain
multipart/form-data
application/x-www/form-urlencoded
请求中,XMLHttpRequest实例没有注册任何事件监听器,即XMLHttpRequest实例对象可以使用XMLHttpRequest.upload属性进行访问
请求中没有使用ReadableStream对象
小结:CORS中主要区分了简单请求和复杂请求两种情况,复杂请求会触发CORS的预检机制。通过上方的案例,也可以清楚CORS的配置主要是在服务端,但客户端也需要知道CORS的使用注意点,例如响应头数据的获取以及cookies的携带配置,这些知识应该是前后端都需要掌握的技能点。
3.服务器代理
同源策略主要是限制浏览器和服务器之间的请求,服务器与服务器之间并不存在跨域。
我们可以通过koa2模拟和实现这种概念:
//前端代码
<body>
<button id="btn"> 请求资源 </button>
<script>
btn.onclick = function () {
let url = checkUrlProxy('http://localhost:9527/api/getMessage','api')
axios.post(url, {
firstName: 'Fred',
lastName: 'Flintstone'
})
.then(function (response) {
console.log(response);
})
}
// 判断接口是否携带api字段,若是,则更改为代理服务器对应的域名
function checkUrlProxy(url, proxyFlag){
let proxyServer = 'http://localhost:1005'
let urlArr = [url.split('/')[1],url.split('/')[3]]
if(urlArr.includes(proxyFlag)) {
return `${proxyServer}/${proxyFlag}${url.split(proxyFlag)[1]}`
}
return url
}
//
</script>
</body>
前端的代码部分,通过checkUrlProxy函数简单地确定本次请求是否要转向代理服务器。
后端代码如下:
//proxyServer.js
let requestFlag = false
let body = ''
app.use(async (ctx, next) => {
// 全放行
if (ctx.method === "OPTIONS") {
ctx.status = 204
requestFlag = false
} else {
requestFlag = true
}
ctx.set("Access-Control-Allow-Origin", "*");
ctx.set("Access-Control-Allow-Credentials", true);
ctx.set("Access-Control-Request-Method", "*");
ctx.set(
"Access-Control-Allow-Headers",
"X-Requested-With,Content-Type,Accept,Origin"
);
ctx.set("Access-Control-Max-Age", 86400);
// 根据具体情况进行修改
ctx.set("Access-Control-Expose-Headers", "With-Requested-Key");
await next();
if(requestFlag) {
ctx.body = body
body = ''
}
});
app.use(async (ctx, next) => {
if (!requestFlag) return
await p4r(ctx)
});
function p4r(ctx) {
return new Promise((res, rej) => {
const proxyRequest = http.request({
host: '127.0.0.1',
port: 9527,
path: ctx.url,
method: ctx.method,
headers: ctx.header
},
serverResponse => {
serverResponse.on('data', chunk => {
body += chunk
})
serverResponse.on('end', () => {
res(body)
})
}
)
proxyRequest.end()
})
}
app.on('error', (err, ctx) => {
console.error('server error', err, ctx)
});
app.listen(1005, (err) => {
if (err) console.log('服务器启动失败');
else console.log('proxy server 1005 running --> ✨✨✨');
})
//targetServer.js
const data = {val : 123}
// 配合代理服务器的post路由
router.post('/api/getMessage', (ctx) => {
ctx.body = JSON.stringify(data)
})
// 定义好路由组件的内容后进行路由注册
app.use(router.routes())
app.on('error', (err, ctx) => {
console.error('server error', err, ctx)
});
app.listen(9527, (err) => {
if (err) console.log('服务器启动失败');
else console.log('服务器启动成功');
})
后端代码主要分两部分:
代理服务器(proxyServer),代理服务器设置CORS时不限制通行,在koa2框架中,通过中间件向目标服务器发送请求,当接收到对应数据后,再响应给浏览器
目标服务器(targetServer),目标服务器不需要做太复杂的配置,案例中只是将数据传递给请求方
Ok,我们通过这个案例,明确代理服务器的具体效果,浏览器向目标服务器直接请求资源,仍然会受到同源策略的影响,但通过代理服务器向目标服务器请求资源时,却没这种限制。
那在实际项目中,我们可以通过脚手架或打包工具的配置文件,简洁方便地设置代理服务器,无需自己手写服务器代码,拿vue的脚手架为例:
devServer:{
proxy:{
'api':{
target:'127.0.0.1:9527', //目标服务器地址
changeOrigin: true, // 是否允许跨域
pathRewrite: { //是否重写接口
'api':'',
}
}
}
}
在配置的时候,可以通过框架的脚手架,或者打包工具确定配置文件,例如一些熟悉的字眼:vue.config.jswebpack.config.jspackage.json(react),更准确的做法就是直接去对应工具的官方文档查阅代理服务器的配置介绍。
相关文章:
前端跨域问题解决
一、同源策略 同源策略是一个重要的安全策略,它用于限制一个Origin的文档或者它加载的脚本如何能与另一个源的资源进行交互。它能帮助阻隔恶意文档,减少可能被攻击的媒介。 Origin:指web文档的来源,Web 内容的来源取决于访问的U…...
【前端】Js
目 录 一.前置知识第一个程序JavaScript 的书写形式注释输入输出 二.语法概览变量的使用理解 动态类型基本数据类型 三.运算符算术运算符赋值运算符 & 复合赋值运算符自增自减运算符比较运算符逻辑运算符位运算移位运算 四.条件语句if 语句三元表达式switch 五.循环语句whi…...
第四章 Istio出口流量管理
文章目录 访问外部服务Envoy 代理将请求传递给网格外服务配置服务条目以提供对外部服务的受控访问访问外部 HTTP 服务 直接访问外部服务 出口网关清理 HTTP 网关其他 访问外部服务 为了更好的做好网络访问控制,k8s结合Istio出口网络升级示意图 来自 Istio 的 pod…...
leetcode做题笔记188. 买卖股票的最佳时机 IV
给你一个整数数组 prices 和一个整数 k ,其中 prices[i] 是某支给定的股票在第 i 天的价格。 设计一个算法来计算你所能获取的最大利润。你最多可以完成 k 笔交易。也就是说,你最多可以买 k 次,卖 k 次。 注意:你不能同时参与多…...
基于springboot实现大学生社团活动平台项目【项目源码+论文说明】
摘要 21世纪的今天,随着社会的不断发展与进步,人们对于信息科学化的认识,已由低层次向高层次发展,由原来的感性认识向理性认识提高,网络管理工作的重要性已逐渐被人们所认识,科学化的管理,使信…...
力扣--第三大的数
给你一个非空数组,返回此数组中 第三大的数 。如果不存在,则返回数组中最大的数。 示例 1: 输入:[3, 2, 1] 输出:1 解释:第三大的数是 1 。 示例 2: 输入:[1, 2] 输出࿱…...
Android 11.0 禁用adb reboot recovery命令实现正常重启功能
1.前言 在11.0的系统开发中,在定制recovery模块的时候,由于产品开发需要要求禁用recovery的相关功能,比如在通过adb命令的 adb reboot recovery的方式进入recovery也需要实现禁用,所以就需要了解相关进入recovery流程来禁用该功能 2.禁用adb reboot recovery命令实现正常…...
Cesium Vue(一)— 项目初始化配置
1. 创建VUE项目工程 创建项目 vue create cesium-vue配置Vue3 2. 创建vue.config.js文件 const { defineConfig } require(vue/cli-service)// The path to the CesiumJS source code const cesiumSource node_modules/cesium/Source; const cesiumWorkers ../Build/C…...
【linux下centos7.9安装docker,docker-composed(root用户)】
一 安装docker 1.联网下载安装docker curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun 2.安装成功 docker -v 3.将docker注册为service,进入/etc/systemd/system/目录,并创建docker.service文件(注意insecure-registry修改成自己的…...
腾讯云我的世界mc服务器配置怎么选择?
使用腾讯云服务器开Minecraft我的世界服务器配置怎么选择?10人以内玩2核4G就够用了,开我的世界服务器选择轻量应用服务器就够了,腾讯云轻量CPU采用至强白金处理器,大型整合包一般1.12版本的,轻量2核4G配置都差不多的&a…...
《低代码指南》——AI低代码维格云架构视图怎么用?
架构视图是一个展示信息层级关系的视图,轻轻拖拽卡片,就能搭建精巧缜密的企业组织架构视图、实现信息结构化。 利用好架构视图,可以很好地解决以下场景: 展示企业/团队的组织关系 可视化管理产品开发架构 统筹全员 OKR 完成情况 架构视图的基础知识 架构视图分为以下几个…...
ubuntu16.04安装vscode遇到的code 依赖于 libnss3 (>= 2:3.30)解决
ubuntu16.04安装vscode遇到的code 依赖于 libnss3 (> 2:3.30);然而:系统中 libnss3:amd64 的版本为 2:3.28.4-0ubuntu0.16.04.14解决方法 一开始下载了最新版本的vscode,安装时出现了上面的错误状况,最新版本的依赖库版本过低的…...
它来了,xhadmin多应用Saas框架正式发布!
这是一个很激动人心的时刻,因为这个框架从立项到现在已经一年多的时间了,先给大家欣赏下颜值吧。 上图所示是总后台的首页截图,看上去还是比较满意的,不过后续还有一些小小的调整。 应用市场的效果也是相当炸裂的,整体…...
PixMIM论文笔记
论文名称:PixMIM: Rethinking Pixel Reconstruction in Masked Image Modeling 发表时间:2023 年 3 月 4 日 作者及组织:上海人工智能实验室、西蒙菲莎大学、香港中文大学 GitHub:https://github.com/open-mmlab/mmselfsup/tree/d…...
设计师首选:最佳的5款网页设计软件
对于UI设计师来说,网页设计是一项必要的技能。如何做好网页设计?除了设计理念,网页设计和制作软件的应用也是不可或缺的。目前市场上有很多这样的软件,工人要想做好,就必须先磨利工具。如果他们想做网页设计࿰…...
ES6箭头函数
1.箭头函数的写法 如果函数内有返回值 var fn v > v 1 返回值如果不是表达式(是对象),用()括号返回 var fn v > ({ obj: v }) 返回值如果不是表达式(也不是对象)方法体内按正常写 var fu () > {var a 1;var b a1;return b; } 2.箭头函数和普通…...
毫米波雷达在环境监测中的关键作用
随着环境问题的日益凸显,精确、实时的环境监测成为了保护地球的关键一环。在这个背景下,毫米波雷达技术逐渐崭露头角,以其在环境监测中的独特优势成为不可或缺的工具。本文将探讨毫米波雷达在环境监测中的关键作用,以及它是如何应…...
print() 函数
二、print() 函数 这里先说一下 print() 函数,如果你是新手,可能对函数不太了解,没关系,在这里你只要了解它的组成部分和作用就可以了,后面函数这一块会详细说明的。 print() 函数由两部分构成 : 指令&a…...
PyQt 小程序
设备管理程序 v0.0.1.0, 终于出了一个基础版本,… … 两个字典的键值判断 辛亏用的是Python 这个编码时间大大缩短了...
大模型技术实践(五)|支持千亿参数模型训练的分布式并行框架
在上一期的大模型技术实践中,我们介绍了增加式方法、选择式方法和重新参数化式方法三种主流的参数高效微调技术(PEFT)。微调模型可以让模型更适合于我们当前的下游任务,但当模型过大或数据集规模很大时,单个加速器&…...
8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂
蛋白质结合剂(如抗体、抑制肽)在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上,高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术,但这类方法普遍面临资源消耗巨大、研发周期冗长…...
学校招生小程序源码介绍
基于ThinkPHPFastAdminUniApp开发的学校招生小程序源码,专为学校招生场景量身打造,功能实用且操作便捷。 从技术架构来看,ThinkPHP提供稳定可靠的后台服务,FastAdmin加速开发流程,UniApp则保障小程序在多端有良好的兼…...
el-switch文字内置
el-switch文字内置 效果 vue <div style"color:#ffffff;font-size:14px;float:left;margin-bottom:5px;margin-right:5px;">自动加载</div> <el-switch v-model"value" active-color"#3E99FB" inactive-color"#DCDFE6"…...
Java-41 深入浅出 Spring - 声明式事务的支持 事务配置 XML模式 XML+注解模式
点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...
【论文阅读28】-CNN-BiLSTM-Attention-(2024)
本文把滑坡位移序列拆开、筛优质因子,再用 CNN-BiLSTM-Attention 来动态预测每个子序列,最后重构出总位移,预测效果超越传统模型。 文章目录 1 引言2 方法2.1 位移时间序列加性模型2.2 变分模态分解 (VMD) 具体步骤2.3.1 样本熵(S…...
企业如何增强终端安全?
在数字化转型加速的今天,企业的业务运行越来越依赖于终端设备。从员工的笔记本电脑、智能手机,到工厂里的物联网设备、智能传感器,这些终端构成了企业与外部世界连接的 “神经末梢”。然而,随着远程办公的常态化和设备接入的爆炸式…...
Web 架构之 CDN 加速原理与落地实践
文章目录 一、思维导图二、正文内容(一)CDN 基础概念1. 定义2. 组成部分 (二)CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 (三)CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 …...
Mobile ALOHA全身模仿学习
一、题目 Mobile ALOHA:通过低成本全身远程操作学习双手移动操作 传统模仿学习(Imitation Learning)缺点:聚焦与桌面操作,缺乏通用任务所需的移动性和灵活性 本论文优点:(1)在ALOHA…...
视觉slam十四讲实践部分记录——ch2、ch3
ch2 一、使用g++编译.cpp为可执行文件并运行(P30) g++ helloSLAM.cpp ./a.out运行 二、使用cmake编译 mkdir build cd build cmake .. makeCMakeCache.txt 文件仍然指向旧的目录。这表明在源代码目录中可能还存在旧的 CMakeCache.txt 文件,或者在构建过程中仍然引用了旧的路…...
vulnyx Blogger writeup
信息收集 arp-scan nmap 获取userFlag 上web看看 一个默认的页面,gobuster扫一下目录 可以看到扫出的目录中得到了一个有价值的目录/wordpress,说明目标所使用的cms是wordpress,访问http://192.168.43.213/wordpress/然后查看源码能看到 这…...
