当前位置: 首页 > news >正文

跨域的几种方案

因为浏览器出于安全考虑,有同源策略。也就是说,如果协议、域名、端口有一个不同就是跨域,Ajax 请求会失败。

我们可以通过以下几种常用方法解决跨域的问题

JSONP

JSONP 的原理很简单,就是利用 <script> 标签没有跨域限制的漏洞。通过 <script> 标签指向一个需要访问的地址并提供一个回调函数来接收数据

涉及到的端

JSONP 需要服务端和前端配合实现。

<script src="http://domain/api?param1=a&param2=b&callback=jsonp"></script>
<script>function jsonp(data) {console.log(data)}
</script>    

JSONP 使用简单且兼容性不错,但是只限于 get 请求

具体实现方式

在开发中可能会遇到多个 JSONP 请求的回调函数名是相同的,这时候就需要自己封装一个 JSONP,以下是简单实现

function jsonp(url, jsonpCallback, success) {let script = document.createElement("script");script.src = url;script.async = true;script.type = "text/javascript";window[jsonpCallback] = function(data) {success && success(data);};document.body.appendChild(script);
}
jsonp("http://xxx","callback",function(value) {console.log(value);}
);

CORS

CORS (Cross-Origin Resource Sharing,跨域资源共享) 是目前最为广泛的解决跨域问题的方案。方案依赖服务端/后端在响应头中添加 Access-Control-Allow-* 头,告知浏览器端通过此请求

涉及到的端

CORS 只需要服务端/后端支持即可,不涉及前端改动

  • CORS需要浏览器和后端同时支持。IE 8 和 9 需要通过 XDomainRequest 来实现。
  • 浏览器会自动进行 CORS 通信,实现CORS通信的关键是后端。只要后端实现了 CORS,就实现了跨域。
  • 服务端设置 Access-Control-Allow-Origin 就可以开启 CORS。 该属性表示哪些域名可以访问资源,如果设置通配符则表示所有网站都可以访问资源。

CORS 实现起来非常方便,只需要增加一些 HTTP 头,让服务器能声明允许的访问来源

只要后端实现了 CORS,就实现了跨域

image.png

以 koa框架举例

添加中间件,直接设置Access-Control-Allow-Origin请求头

app.use(async (ctx, next)=> {ctx.set('Access-Control-Allow-Origin', '*');ctx.set('Access-Control-Allow-Headers', 'Content-Type, Content-Length, Authorization, Accept, X-Requested-With , yourHeaderFeild');ctx.set('Access-Control-Allow-Methods', 'PUT, POST, GET, DELETE, OPTIONS');if (ctx.method == 'OPTIONS') {ctx.body = 200; } else {await next();}
})

具体实现方式

CORS 将请求分为简单请求(Simple Requests)和需预检请求(Preflighted requests),不同场景有不同的行为

简单请求:不会触发预检请求的称为简单请求。当请求满足以下条件时就是一个简单请求:

  • 请求方法:GET、HEAD、POST。
  • 请求头:Accept、Accept-Language、Content-Language、Content-Type。
    • Content-Type 仅支持:application/x-www-form-urlencoded、multipart/form-data、text/plain

需预检请求:当一个请求不满足以上简单请求的条件时,浏览器会自动向服务端发送一个 OPTIONS 请求,通过服务端返回的Access-Control-Allow-* 判定请求是否被允许

CORS 引入了以下几个以 Access-Control-Allow-* 开头:

  • Access-Control-Allow-Origin 表示允许的来源
  • Access-Control-Allow-Methods 表示允许的请求方法
  • Access-Control-Allow-Headers 表示允许的请求头
  • Access-Control-Allow-Credentials 表示允许携带认证信息

当请求符合响应头的这些条件时,浏览器才会发送并响应正式的请求

nginx反向代理

反向代理只需要服务端/后端支持,几乎不涉及前端改动,只用切换接口即可

nginx 配置跨域,可以为全局配置和单个代理配置(两者不能同时配置)

全局配置,在nginx.conf文件中的 http 节点加入跨域信息

http {# 跨域配置add_header 'Access-Control-Allow-Origin' '$http_origin' ;add_header 'Access-Control-Allow-Credentials' 'true' ;add_header 'Access-Control-Allow-Methods' 'PUT,POST,GET,DELETE,OPTIONS' ;add_header 'Access-Control-Allow-Headers' 'Content-Type,Content-Length,Authorization,Accept,X-Requested-With' ;
}

局部配置(单个代理配置跨域), 在路径匹配符中加入跨域信息

server {listen       8080;server_name  server_name;charset utf-8;location / {# 这里配置单个代理跨域,跨域配置add_header 'Access-Control-Allow-Origin' '$http_origin' ;add_header 'Access-Control-Allow-Credentials' 'true' ;add_header 'Access-Control-Allow-Methods' 'PUT,POST,GET,DELETE,OPTIONS' ;add_header 'Access-Control-Allow-Headers' 'Content-Type,Content-Length,Authorization,Accept,X-Requested-With' ;#配置代理 代理到本机服务端口proxy_pass http://127.0.0.1:9000;proxy_redirect   off;proxy_set_header Host $host:$server_port;proxy_set_header X-Real-IP $remote_addr;proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;}
}

Node 中间层接口转发

const router = require('koa-router')()
const rp = require('request-promise');// 通过node中间层转发实现接口跨域
router.post('/github', async (ctx, next) => {let {category = 'trending',lang = 'javascript',limit,offset,period} = ctx.request.body lang = lang || 'javascript'limit = limit || 30offset = offset || 0period = period || 'week'let res =  await rp({method: 'POST',// 跨域的接口uri: `https://e.juejin.cn/resources/github`,body: {category,lang,limit,offset,period},json: true})ctx.body = res
})module.exports = router

Proxy

如果是通过vue-cli脚手架工具搭建项目,我们可以通过webpack为我们起一个本地服务器作为请求的代理对象

通过该服务器转发请求至目标服务器,得到结果再转发给前端,但是最终发布上线时如果web应用和接口服务器不在一起仍会跨域

在vue.config.js文件,新增以下代码

module.exports = {devServer: {host: '127.0.0.1',port: 8080,open: true,// vue项目启动时自动打开浏览器proxy: {'/api': { // '/api'是代理标识,用于告诉node,url前面是/api的就是使用代理的target: "http://xxx.xxx.xx.xx:8080", //目标地址,一般是指后台服务器地址changeOrigin: true, //是否跨域pathRewrite: { // pathRewrite 的作用是把实际Request Url中的'/api'用""代替'^/api': "" }}}}
}

通过axios发送请求中,配置请求的根路径

axios.defaults.baseURL = '/api'

此外,还可通过服务端实现代理请求转发,以express框架为例

var express = require('express');
const proxy = require('http-proxy-middleware')
const app = express()
app.use(express.static(__dirname + '/'))
app.use('/api', proxy({ target: 'http://localhost:4000', changeOrigin: false}));
module.exports = app

websocket

webSocket本身不存在跨域问题,所以我们可以利用webSocket来进行非同源之间的通信

原理:利用webSocket的API,可以直接new一个socket实例,然后通过open方法内send要传输到后台的值,也可以利用message方法接收后台传来的数据。后台是通过new WebSocket.Server({port:3000})实例,利用message接收数据,利用send向客户端发送数据。具体看以下代码:

function socketConnect(url) {// 客户端与服务器进行连接let ws = new WebSocket(url); // 返回`WebSocket`对象,赋值给变量ws// 连接成功回调ws.onopen = e => {console.log('连接成功', e)ws.send('我发送消息给服务端'); // 客户端与服务器端通信}// 监听服务器端返回的信息ws.onmessage = e => {console.log('服务器端返回:', e.data)// do something}return ws; // 返回websocket对象
}
let wsValue = socketConnect('ws://121.40.165.18:8800'); // websocket对象

document.domain(不常用)

  • 该方式只能用于二级域名相同的情况下,比如 a.test.com 和 b.test.com 适用于该方式。
  • 只需要给页面添加 document.domain = ‘test.com’ 表示二级域名都相同就可以实现跨域
  • 自 Chrome 101 版本开始,document.domain 将变为可读属性,也就是意味着上述这种跨域的方式被禁用了

postMessage(不常用)

在两个 origin 下分别部署一套页面 A 与 B,A 页面通过 iframe 加载 B 页面并监听消息,B 页面发送消息

这种方式通常用于获取嵌入页面中的第三方页面数据。一个页面发送消息,另一个页面判断来源并接收消息

// 发送消息端
window.parent.postMessage('message', 'http://test.com');
// 接收消息端
var mc = new MessageChannel();
mc.addEventListener('message', (event) => {var origin = event.origin || event.originalEvent.origin;if (origin === 'http://test.com') {console.log('验证通过')}
});

window.name(不常用)

主要是利用 window.name 页面跳转不改变的特性实现跨域,即 iframe 加载一个跨域页面,设置 window.name,跳转到同域页面,可以通过 $(‘iframe’).contentWindow.name 拿到跨域页面的数据

实例说明

比如有一个www.example.com/a.html页面。需要通过a.html页面里的js来获取另一个位于不同域上的页面www.test.com/data.html中的数据。

data.html页面中设置一个window.name即可,代码如下

<script>window.name = "我是data.html中设置的a页面想要的数据";
</script>
  • 那么接下来问题来了,我们怎么把data.html页面载入进来呢,显然我们不能直接在a.html页面中通过改变window.location来载入data.html页面(因为我们现在需要实现的是a.html页面不跳转,但是也能够获取到data.html中的数据)
  • 具体的实现其实就是在a.html页面中使用一个隐藏的iframe来充当一个中间角色,由iframe去获取data.html的数据,然后a.html再去得到iframe获取到的数据。
  • 充当中间人的iframe想要获取到data.html中通过window.name设置的数据,只要要把这个iframe的src设置为www.test.com/data.html即可,然后a.html想要得到iframe所获取到的数据,也就是想要得到iframe的widnow.name的值,还必须把这个iframe的src设置成跟a.html页面同一个域才行,不然根据同源策略,a.html是不能访问到iframe中的window.name属性的
<!-- a.html中的代码 -->
<iframe id="proxy" src="http://www.test.com/data.html" style="display: none;" onload = "getData()"> <script>function getData(){var iframe = document.getElementById('proxy);iframe.onload = function(){var data = iframe.contentWindow.name;//上述即为获取iframe里的window.name也就是data.html页面中所设置的数据;}iframe.src = 'b.html'; //这里的b为随便的一个页面,只有与a.html同源就行,目的让a.html等访问到iframe里的东西,设置成about:blank也行}
</script>

上面的代码只是最简单的原理演示代码,你可以对使用js封装上面的过程,比如动态的创建iframe,动态的注册各种事件等等,当然为了安全,获取完数据后,还可以销毁作为代理的iframe

补充

跨域与监控

前端项目在统计前端报错监控时会遇到上报的内容只有 Script Error 的问题。这个问题也是由同源策略引起。在 <script> 标签上添加 crossorigin=“anonymous” 并且返回的 JS 文件响应头加上 Access-Control-Allow-Origin: * 即可捕捉到完整的错误堆栈

跨域与图片

前端项目在图片处理时可能会遇到图片绘制到 Canvas 上之后却不能读取像素或导出 base64 的问题。这个问题也是由同源策略引起。解决方式和上文相同,给图片添加 crossorigin=“anonymous” 并在返回的图片文件响应头加上 Access-Control-Allow-Origin: * 即可解决

http://www.xdnf.cn/news/449371.html

相关文章:

  • MySQL 存储函数[特殊字符] VS 存储过程[特殊字符]
  • 二手车估值接口介绍
  • sql sql复习
  • python如何设置excel单元格边框样式
  • C++ 在 Windows 的开发经验与解决方案
  • 【Linux网络】TCP全连接队列
  • Android学习总结之kotlin篇(二)
  • 更换git位置并在pycharm中重新配置
  • Vue.js 组件开发指南
  • 力扣144题:二叉树的前序遍历(递归)
  • 倍福 TC3 PID 功能块 引脚功能及PID控制用法
  • windows运行bat闪退
  • 「Mac畅玩AIGC与多模态37」开发篇32 - 基于工作流的双插件信息整合与展示优化
  • 抢跑「中央计算+区域控制」市场,芯驰科技高端智控MCU“芯”升级
  • 微机原理与接口技术知识点总结——8086微处理器ddddd
  • C++红黑树
  • Redis的Pipeline和Lua脚本适用场景是什么?使用时需要注意什么?
  • PH热榜 | 2025-05-14
  • 《AI大模型应知应会100篇》第62篇:TypeChat——类型安全的大模型编程框架
  • 【面试 · 五】CSS个别重点总结
  • 论系统安全架构设计及其应用~系统架构师论文
  • 三种常见接口测试工具(Apipost、Apifox、Postman)
  • 【NLP 计算句子之间的BLEU和ROUGE分数】
  • 代理IP与VPN的区别,如何根据需求选择?
  • Vector和list
  • FastAPI + OpenAI 模型 的 GitHub 项目结构模板
  • OPC UA + ABP vNext 企业级实战:高可用数据采集框架指南
  • 基于OAuth2+SpringSecurity+Jwt实现身份认证和权限管理后端服务
  • 自注意力机制(Self-Attention)前向传播手撕
  • 记录一次git提交失败解决方案