news 2026/5/9 8:32:32

跨域及其解决方案

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
跨域及其解决方案

1.不得不说的同源策略

一说跨域,一定会先说同源策略,有时间的可以看一下官方解释的同源策略

浏览器的同源策略 - Web 安全 | MDN

同源策略同源策略是浏览器的一个安全行为,是指浏览器对不同源的脚本或文本的访问方式进行限制。(ajax请求时,浏览器要求当前网页和server必须同源)

那么什么是同源呢?

同源:指两个页面具有相同的协议,域名,端口号

为什么需要同源策略?

浏览器为了保护用户的数据安全,尽可能阻止跨域攻击,浏览器是公共资源,而一个网站是私有资源。假设没有同源策略,A网站的API可以被任意来源的ajax请求访问,包括了用户的隐私信息。会出现很大的问题。

举个栗子:

如果你登录淘宝,淘宝给你返回了一个cookie,下次请求你会带上cookie,这样子服务器就知道你登录过了,假设,你买东西过程中,又点来了一个链接,由于没有同源策略,他就在后台操作向淘宝发起请求,那么就相当于不法网站利用你的账号进行为所欲为了,假设这个账号是个银行账号,你的钱可能已经没有了

同源策略限制的不同源之间的交互,主要是针对js中的XMLHttpRequest请求,有一些情况是不受影响的如:html的一些标签的请求,链接标签A,图片标签Img,script标签,这些标签的请求可以为不同源地址

那同源策略到底限制了哪些行为?

  • Cookie、LocalStorage 和 IndexDB 无法读取
  • DOM 和 JS 对象无法获取
  • Ajax请求发送不出去

2.什么是跨域 ?

因为浏览器存在了同源策略导致跨域,那什么是跨域,协议,域名(主机号),端口号有一个不同就是跨域

案例:

跨域问题会在各种情况出现,当正常进行访问时,需要解决跨域带来的问题,那跨域有哪些解决方案呢??

3.哪些无视了同源策源?

<img src=跨域的图片地址> //可用于统计打点,统计第三的点击次数 <lik href=跨域的css地址> //CDN <script src=跨域的js地址> //jsop的实现原理

4.实现跨域之前

所有的跨域,都必须经过server端的允许和配合

未经server允许就实现跨域,说明浏览器有漏洞,账号密码就很容易泄露

5.如何解决跨域?

(1) JSONP - 前后端配合

首先需要知道的,服务器端可以任意动态拼接数据返回,只要符合html格式,同理script标签的src也是服务器端可以任意返回一个动态拼接的数据

为了减轻web服务器的资源,前端的js,html,img这些资源都是被放在独立域名的服务器上的,前面也提了,同源策略并没有对这些标签进行限制,而jsonp就是利用scirpt标签进行请求,由于是src里面写地址,因此jsonp只能发送get请求,并且jsonp需要后端进行配合,返回一个可执行的方法callback给前端,最后实现数据的加载,jsonp不是一个请求,只是一个js的脚本,因此你在xhr中是看不到这个请求的

优点:简单,兼容性比较好(包括了低版本的ie)

缺点:只能发送get请求

原生实现:

<script> let script = document.createElement('script'); script.type 'text/javascript'; //请求接口地址和参数 script.src = 'http://wwww.ceshi.com/login?username=aaa&callback=callback'; document.body.appendChild(script); //请求后的回调函数 function callback(res) { console.log(res) } </script>

jQuery

$.ajax({ url: "http://wwww.ceshi.com/login", type: 'get', dataType: 'jsonp', jsonpCallback: 'callback', data: { 'username': 'aaa' } })

vue.js

下载安装插件

npm install vue-jsonp --save

main.js中引用

import Vue from 'vue' import VueJsonp from 'vue-jsonp' Vue.use(VueJsonp)

使用

created(){ this.tosend(); }, mounted(){ window.jsonpCallback = (res) => { console.log(res) } }, methods: { tosend(){ this.$jsonp('http://wwww.ceshi.com/login', { params: { username: 'aaa' }, jsonp: 'jsonpCallback' //这个回调函数才是jsonp返回的数据 }) .then((res) => { console.log(res) }) } }

(2) document.domain + iframe

浏览器可以通过domain查看当前的文档的服务器域,检查两个网页是否同源,强制设置domain为同一个域,就可以实现同域,利用这个,可以实现两个页面共用cookie,也可以进行数据的传递等

缺点:只适用于两个页面属于同一基础域,且协议和端口号相同

像:aa.qq.com, bb.qq.com,cc.qq.com,他们都有公共的上级域名qq.com

又比如:主域名:www.test.com ,子域名为child.test.com,对于两个页面,同时设置

document.domain = 'test.com'

则就可以各自进行访问window对象了

例如:

www.test.com

<iframe src="http://child.test.com/index.html"></iframe> <script> document.domain = 'test.com'; var name = 'a' </script>

childe.test.com

<script> document.domain = 'test.com' consloe.log(window,parent.name) </script>

(3) postMessage

官方解释: window.postMessage - Web API 接口参考 | MDN

postMessgae是h5引入的API,它可以允许不同源的脚本采用异步的方式进行通信,可以实现

  • 多窗口的数据通信
  • 跨域消息的传递,跨域存储,
  • 页面与嵌套的frame之间消息传递

缺点:存在一定安全问题

如果您不希望从其他网站接收message,请不要为message事件添加任何事件侦听器。如果您确实希望从其他网站接收message,请始终使用origin和source属性验证发件人的身份。 任何窗口都可以向任何其他窗口发送消息,并且您不能保证未知发件人不会发送恶意消息。

先讲解一下api

发送信息

otherWindow.postMessage(message, targetOrigin, [transfer]);

otherWindow- iframe的contentWindow属性,执行window.open返回的窗口对象,或者是命名过或数值索引的window.frames

message -发送给其他页面的数据

targetOrigin- 指定哪些窗口可以接受传递的数据,可以是字符串“*”(所有页面可接受)或者是一个url,如果是明确消息发送的窗口,就提供一个确切的url,不提供确切的目标将导致数据泄露到任何对数据感兴趣的恶意站点。

**transfer**可选 - 是一串和message 同时传递的

Transferable

对象. 这些对象的所有权将被转移给消息的接收方,而发送一方将不再保有所有权。

接收数据

// 监听 message 消息 window.addEventListener('message', function (e) { console.log(e.source); // e.source 发送消息的窗口 console.log(e.origin); // e.origin 消息发向的网址 console.log(e.data); // e.data 发送的消息},false);

案例:

www.test.com

<p></p> <iframe id='iframe' src="http://www.test.com/index.html"></iframe> <script> window.onload = function(){ document.getElementById('iframe').postMessage('hello','http://www.test2.com'); } // 主页面监听message事件,将返回的数据写入p window.addEventListener('message', function(e){ document.querySelector('p').innerHTML = e.data; }, false); </script>

www.test2.com

<script> window.addEventListener('message', function(e) { if (e.source != window.parent) return; //保存来自父亲的消息 localStorage.setItem('fromParent',e.data); //向父亲传递消息,已接收 window.parent.postMessage('finished', '*'); }, false); </script>

(4)CORS - 服务器设置http header

这是一个纯服务器端操作 - 可以看这个部分的最后,服务器端只要设置好了就行了,前端只需要正常请求就ok了

CORS是W3C标准,允许浏览器向跨源服务器发送XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制,并且需要浏览器和服务器同时支持,目前所有的浏览器都支持该功能除了IE,IE浏览器不能低于IE10,是 JSONP 模式的现代版。与 JSONP 不同,CORS 除了 GET 要求方法以外也支持其他的 HTTP 要求

CORS官方文档:跨源资源共享(CORS) - HTTP | MDN

阮一峰大神对CORS的详细讲解:跨域资源共享 CORS 详解 - 阮一峰的网络日志

实现的原理是自定义HTTP的头部允许浏览器和服务器互相了解对方,从而去决定请求和响应成功与否

后端设置后,可以在返回中看到

HTTP请求头部字段

Orgin- 表明预检请求或者实际请求的源站,值为源站的url,不包含任何路径信息,只是服务器的名称,就是HTTP请求头,涉及到CORS请求都必需携带

Access-Control-Request-Method- 实际请求所使用的HTTP方法告诉服务器

Access-Control-Request-Headers -将要发起的跨域请求中包含的请求头字段

服务器在响应字段中来表明是否允许这个跨域请求,浏览器收到后检查如果不符合要求,就拒绝后面的请求

HTTP响应字段

Access-Control-Allow-Origin -允许哪些域来访问(*表示允许所有域的请求)

Access-Control-Allow-Methods- 允许哪些请求方式

Access-Control-Allow-Headers -允许哪些请求头字段

Access-Control-Allow-Credentials -是否允许携带Cookie

对于普通跨域请求 - 只需要服务器端进行设置Access-Control-Allow-Origin就可以了

如果是cookie的跨域请求,就需要前后端都需要设置,后端已经开启CORS,前端需要也携带cookie,此时需要在前端请求头加上withCredentials: true

原生

function tosend() { var xhr = new XMLHttpRequest(); //前端是否携带token xhr.withCredentials = true; xhr.open("post", "http://wwww.test.com/login", true); xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); // 声明请求源 xhr.setRequestHeader("Origin", "http://wwww.test.com"); xhr.send(); xhr.onreadystatechange = function () { if (xhr.readyState == 4 && xhr.status == 200) { var responseText = xhr.responseText; console.info(responseText); } } }

jQuery

$.ajax({ type: 'post', crossDomain: true, url: 'http://wwww.test.com/login', data: { UserName: 'aa', }, dataType:'json', xhrFields: { withCredentials: true }, success: function(data, textStatus, jqXHR){ } });

vue

vue-resurce

Vue.http.options.credentials = true

axios

axios.defaults.withCredentials = true

后端要怎么开启cors?那是后端的事情了

但是其实只需要后端添加相应的字段就行了

/* * 导入包:import javax.servlet.http.HttpServletResponse; * 接口参数中定义:HttpServletResponse response */ // 允许跨域访问的域名:若有端口需写全(协议+域名+端口),若没有端口末尾不用加'/' response.setHeader("Access-Control-Allow-Origin", "http://www.domain1.com"); // 允许前端带认证cookie:启用此项后,上面的域名不能为'*',必须指定具体的域名,否则浏览器会提示 response.setHeader("Access-Control-Allow-Credentials", "true"); // 提示OPTIONS预检时,后端需要设置的两个常用自定义头 response.setHeader("Access-Control-Allow-Headers", "Content-Type,X-Requested-With");

(5) nginx代理跨域

nginx代理跨域,需要进行修改配置文件,

nginx实现简单代理_哆来A梦没有口袋的博客-CSDN博客_nginx代理怎么做

(6) proxy

直接在配置文件,vue.config.js中,vue的webpack的配置文件,遵循的common.js的规范,nodejs去转发服务

module.exports = { devServer: { host: 'localhost', host: '0.0.0.0', port: '8081', before: (app) => {//控制台查看是否成功,正常情况都可以不需要这个函数,只是辅助开发 var url = require('url'); app.use((req, res, next) => { function fullUrl(req) { return url.format({ protocol: req.protocol, host: req.get('host'), pathname: req.originalUrl }); } console.log("req.url", fullUrl(req)); next() }) }, proxy: { //代理服务器,一般使用mock联调 与 vue-cli2.x的 proxyTable 一样的功能 // '/': { // target: "http://cqasia.860001.xyz:12091", // }, "/dima_server/api": { target: 'https://cqasia.860001.xyz:12092', //代理的地址 pathRewrite: { "^/dima_server/api": "/dima_server/api" }, //路径转发规则 ws: false, //如果是要代理websockets,需要边为true changeOrigin: true,//是否改变源,告诉服务器端真实地址 }, } }
6.1 porxy为什么可以解决跨域呢?

跨域是浏览器行为,porxy的执行如下

浏览器 (localhost:8080) ↓ 发起请求(仍然到同源的 8080 端口) Vue Dev Server (localhost:8080) ↓ 识别出需要代理的路径 ↓ 服务器端向目标 API 发起真实请求(没有跨域限制) 目标 API 服务器 (api.example.com) ↓ 返回数据给 Vue Dev Server Vue Dev Server ↓ 将数据返回给浏览器 浏览器接收数据(由于同源,浏览器认为安全)

Vue 的proxy只在开发环境下生效,当你运行npm run serveyarn serve时。

当你执行npm run build打包部署到生产环境后:

  • 前端文件变成静态文件(HTML/JS/CSS)

  • 不再有 Node.js 开发服务器

  • proxy配置不再生效

  • 生产环境的跨域问题需要通过其他方式解决(如:后端配置 CORS、Nginx 反向代理等)

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/9 8:32:31

从零搭建Telegram Bot自动化助手:Node.js+Telegraf实战指南

1. 项目概述与核心价值最近在折腾一个挺有意思的项目&#xff0c;叫yalexx/openclaw-telegram-setup-guide。乍一看这个名字&#xff0c;可能有点摸不着头脑&#xff0c;但说白了&#xff0c;这就是一份教你如何把 Telegram 这个即时通讯工具&#xff0c;变成一个功能强大、高度…

作者头像 李华
网站建设 2026/5/9 8:31:31

5分钟掌握全能资源嗅探:解锁网页媒体自由下载的终极方案

5分钟掌握全能资源嗅探&#xff1a;解锁网页媒体自由下载的终极方案 【免费下载链接】cat-catch 猫抓 浏览器资源嗅探扩展 / cat-catch Browser Resource Sniffing Extension 项目地址: https://gitcode.com/GitHub_Trending/ca/cat-catch 还在为无法保存在线视频资源而…

作者头像 李华
网站建设 2026/5/9 8:30:36

PUBG绝地求生压枪脚本终极指南:5步实现罗技鼠标精准射击

PUBG绝地求生压枪脚本终极指南&#xff1a;5步实现罗技鼠标精准射击 【免费下载链接】logitech-pubg PUBG no recoil script for Logitech gaming mouse / 绝地求生 罗技 鼠标宏 项目地址: https://gitcode.com/gh_mirrors/lo/logitech-pubg 在《绝地求生》这款硬核射击…

作者头像 李华
网站建设 2026/5/9 8:20:36

基于MCP协议构建Notion加速服务器:架构设计与性能优化实践

1. 项目概述&#xff1a;一个为Notion提速的MCP服务器如果你和我一样&#xff0c;重度依赖Notion来管理日常工作流、知识库甚至是个人生活&#xff0c;那你一定对它的速度有过那么一丝丝的“怨念”。尤其是在处理包含大量数据库、复杂视图或嵌入内容的页面时&#xff0c;偶尔的…

作者头像 李华
网站建设 2026/5/9 8:20:29

如何用 structuredClone 原生函数实现复杂对象深拷贝

structuredClone 是浏览器原生深拷贝方法&#xff0c;支持 Map、Set、Date、RegExp、ArrayBuffer 等复杂类型及循环引用&#xff0c;不支持函数、Promise、WeakMap 等&#xff1b;可通过 transfer 选项高效转移 ArrayBuffer 内存&#xff1b;Chrome 98、Firefox 97、Safari 15.…

作者头像 李华