一文带你理清同源和跨域
csdh11 2024-12-02 16:38 4 浏览
1、概述
前后端数据交互经常会碰到请求跨域,什么是跨域,为什么需要跨域,以及常用有哪几种跨域方式,这是本文要探讨的内容。
同源策略(英文全称 Same origin policy)是浏览器提供的一个安全功能。同源策略限制了从同一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的重要安全机制。
同源策略是一种约定,它是浏览器最核心也是最基本的安全功能。出于安全考虑,浏览器限制从JS脚本发起的跨源HTTP请求。
通俗的理解:浏览器规定,A 网站的 JavaScript,不允许和非同源的网站 C 之间,进行资源的交互,例如:
①无法读取非同源网页的 Cookie、LocalStorage 和 IndexedDB。
②无法接触非同源网页的 DOM。
③无法向非同源地址发送 Ajax 请求。
同源指的是两个 URL 的协议、域名、端口一致,反之,则是跨域。出现跨域的根本原因:浏览器的同源策略不允许非同源的 URL 之间进行资源的交互。
例如网页(http://www.test.com/index.html)和接口(http://www.api.com/userlist),非同源的URL,浏览器允许发起跨域请求,但是,跨域请求回来的数据,会被浏览器拦截,无法被页面获取到。
2、为什么要跨域?
跨域是浏览器受同源(协议、域名、端口)策略的限制,不允许不同源的站点之间进行某些操作(如发送ajax请求,操作dom,读取cookie),如果不进行特殊配置是不能操作成功的,并且控制台会报如下跨域错误:
`No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'xxxxxx' is therefore not allowed access
跨域的根本原因是浏览器的“同源策略”,同源 就是【协议+域名+端口号】相同,即为同源,只能向同源的服务发起AJAX请求。
源1 | 源2 | 是否同源 |
a.com | b.com | 不同源,域名不同 |
http://a.com | https://a.com | 不同源,协议不同 |
a.com:80 | a.com:443 | 不同源,端口不同 |
gg.com | a.gg.com | 不同源,子域名不同 |
a.com/ss | a.com/s2 | 同源 |
可通过location.origin、window.origin获取当前文档的源
为什么要同源呢?
这是浏览器故意设计的,是浏览器的基本安全策略,否则会很容易受到XSS、CSRF攻击。只能向同源的服务发起AJAX请求,不可跨域请求,会被浏览器拦截。
有哪些限制规则呢?
- ? 访问其他源的图片、CSS、JS是可以的,允许<img src="url">、<link href="url">、<script src="url">元素获取的其他源的资源。
- ? Form表单可以跨域提交,表单的提交只是提交数据无需返回,浏览器认为是安全的。
- AJAX不可以向其他源发送网络请求,会被浏览器拦截。注意拦截的不是请求,而是响应,服务端依然是可以收到请求的。
- 仅可访问自己域的cookie、localStorage、DOM树,不能访问Iframe嵌入的其他页面内部内容。
3、如何实现跨域?
跨域请求(Cross-Origin Request),简称CORS,是指在Web开发中,当一个Web页面向不同源(域名、协议或端口)的服务器发起请求时,浏览器会遵循同源策略(Same-Origin Policy)的限制,对这些跨源请求进行限制。由于浏览器的同源策略限制,跨域请求默认是被禁止的,同源策略要求请求的协议、域名和端口必须完全一致,否则会被浏览器拦截。
随着互联网越来越复杂,需求也越来越多,跨域请求就很常见了。我们知道了跨域是浏览器的同源限制,就可以针对性的想办法了。
本文主要针对最常用的三种进行讲解,其他的可以自行参考相关文章。
3.1、JSONP跨域
这是一种传统的跨域请求办法,借助于<script>标签元素,因为<script>的src可以访问任何站点的资源。当然这需要服务端对应接口支持JSONP(JSON with padding)协议,所以是需要双方约定好,所以浏览器认为这是安全的。
- 优点是兼任IE,实现跨域。
- 缺点是不能控制请求过程,仅支持GET方式请求。因为只是一个<script>标签,浏览器自动发起的资源请求。
JSONP(JSON with Padding)是JSON的一种”使用模式“,是一种非官方的协议,用于解决浏览器的跨域数据访问的问题。
前端具体实现过程:
- 1、申明一个全局的回调函数“getData”来接收数据。
- 2、动态创建一个<script>标签,src为要跨域的API地址,URL中带上回调参数“callback=getData”。
- 3、服务端收到请求后,动态生成一个脚本,脚本内容是一个字符串,由回调+返回的数据构成:“getData('data')”。
- 4、本地执行远程脚本,回调函数“getData”运行,就得到了想要的数据。
<script src="http:www.thrid.com/cors/api?q=key&callback=back"></script>
<script>
function back(data) {
console.log(data);
}
</script>
JSONP的实现:
function jsonp(url, args, cbName) {
return new Promise((resolve, reject) => {
const ele = document.createElement('script');
window[cbName] = (data) => {
resolve(data);
document.body.removeChild(ele);
}
args = { ...args, callback: cbName };
ele.src = `${url}?${Object.keys(args).map(k => `${k}=${args[k]}`).join('&')}`;
document.body.appendChild(ele);
});
}
//使用,api为360的公开接口
jsonp('https://sug.so.360.cn/suggest', { format: 'jsonp', word: 'china' }, 'search')
.then(function (data) {
console.log(data)
});
3.2、CORS跨域
CORS是什么?—— 跨域资源共享 (cross-origin resource sharing),让AJAX可以跨域访问数据。这是为了满足跨域请求的需求,W3C新增加的特性,需要服务端的支持,不支持IE8/9。
当浏览器发送一个跨域请求时,它会首先发送一个预检请求(OPTIONS请求),检查后端是否支持跨域请求。这个预检请求会包含一些CORS相关的HTTP头,如Origin、Access-Control-Request-Method和Access-Control-Request-Headers。后端收到预检请求后,会检查请求中的Origin头,与自己在CORS配置中设置的allowedOrigins进行对比,如果匹配成功,就会在响应中设置相应的CORS头,如Access-Control-Allow-Origin、Access-Control-Allow-Methods和Access-Control-Allow-Headers等。
一旦预检请求通过,浏览器就会发送实际的跨域请求。在接收到实际请求的响应后,浏览器会再次检查响应中的CORS头,确保它们与预检请求中的设置一致。如果一切正常,浏览器就会允许前端JavaScript代码访问响应中的数据。
通过这种方式,后端通过显式配置CORS参数,告知浏览器哪些源有权访问其资源,从而实现了跨域访问的功能。这既保证了安全性(只允许指定的源进行访问),又提供了灵活性(可以根据需要配置不同的CORS策略)。
根据请求方式,浏览器将CORS分为两种情况:
- 简单请求(安全请求):只支持GET、POST、HEAD,Header只支持部分字段。
- 复杂请求(其他请求):简单请求以外的其他跨域请求。
简单请求
基本原理就是在请求头加入一个身份来源标识,服务端根据这个标识来判等是否允许访问,如果允许则给一个允许的标记并返回响应。
- 只支持GET、POST、HEAD。
- header —— 我们仅能设置基础的安全字段:
- Accept
- Accept-Language
- Content-Language
- Content-Type 的值为 application/x-www-form-urlencoded,multipart/form-data 或 text/plain。
具体过程比较简单,前端只要在Header加入“Origin”即可:
- 请求头Header加入要跨域的源:origin:http://www.main.com
GET /api HTTP/1.1
Origin: http://www.main.com //本次请求来自哪个源
Host: http://www.third.com //请求的第三方API
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0
...
- 服务端收到请求后检查Origin,如果同意请求则正常响应,同时在响应的Header中加入特殊的“Access-Control-Allow-Origin”字段,申明支持的源,也可以用“*”表示支持任何源访问。
- 浏览器收到响应后会检查“Access-Control-Allow-Origin”,和当前源对比,如果不合法则会报错——跨域。
Access-Control-Allow-Origin: http://www.main.com //请求允许的源
Access-Control-Allow-Credentials: true //是否允许cookie,cors默认不发送cookie,如果要发送,还需AJAX中设置withCredentials
Access-Control-Expose-Headers: Content-Length,API-Key //如果客户端想要访问其他非安全字段,则需要服务端明确定义哪些Header字段暴露出来
Content-Type: text/html; charset=utf-8
复杂请求
不是简单请求的都称为复杂请求(非简单请求),如请求方法是PUT、DELETE,或Content-Type=application/json。相比于简单请求,复杂请求多了一次预请求。
预请求:
- 正式发送请求前,浏览器会自动发送一个预请求,问问服务端是否允许本次请求,如果回应允许才正式发送请求,后面就和简单请求相同了。
- 预请求及其响应都没有body,采用OPTIONS方法。
JSONP 与`CORS 的对比
JSONP 是很早很成熟的解决方案,但是,只能进行 GET 请求,无法实现上传数据等操作。
反观:CORS 虽然分 预请求 和 非预请求 ,但是,无疑支持的功能是非常强大的 !!!
3.3、Nginx反向代理
跨域是浏览器的保护机制,如果绕过浏览器,使用代理服务器去请求目标服务器上的数据,就不会受跨域影响。因此前端可以通过脚手架或webpack配置devSever下的proxy选项,将/api开头的请求转发到真实服务器上。
在生产环境下也可以使用nginx配置反向代理来解决跨域。
Nginx 则是通过反向代理的方式,(这里也需要自定义一个域名)这里就是保证我当前域,能获取到静态资源和接口,不关心是怎么获取的。
配置下 hosts
127.0.0.1 local.test
配置 nginx
server {
listen 80;
server_name local.test;
location /api {
proxy_pass http://localhost:8080;
}
location / {
proxy_pass http://localhost:8000;
}
}
对于前端开发而言,大部分的跨域问题,都是通过代理解决的
代理适用的场景是:生产环境不发生跨域,但开发环境发生跨域
4、小结
因为同源是浏览器的限制,跨域的方法无非就是绕过,或采用CORS。
跨域方案 | 基本原理 | 是否需要服务端支持 |
JSONP | 借助<script>标签的src,加上一个全局回调函数接收数据 | 需要服务端支持JSONP协议 |
CORS | W3C标准支持的跨域方式,请求头添加Origin字段 | 需要服务端支持 |
WebSocket | WebSocket可以实现浏览器与服务端的双向通信,没有跨域的困惑。推荐第三方库 Socket.io,可以很方便的建立与服务端的Socket通信。 | 需要服务端支持,支持WebSocket |
iframe+postMessage | 使用window.postMessage()来实现窗口之间的通信 | 不需服务端处理,客户端绕过 |
服务端代理 | 由自己的同源服务端代理第三方的请求 | 需要服务端支持,代理请求 |
nginx反向代理 | 原理和服务端代理一样,用nginx配置一个代理服务 | 不需要服务端修改代码,需nginx支持 |
- CORS支持所有类型的HTTP请求,是跨域HTTP请求的根本解决方案。
- JSONP只支持GET请求,JSONP的优势在于支持老式浏览器,以及可以向不支持CORS的网站请求数据。
- 不管是Node中间件代理还是nginx反向代理,主要是通过同源策略对服务器不加限制。
- 日常工作中,用得比较多的跨域方案是cors和nginx反向代理。
5、参考文章
So, JSONP or CORS? - Stack Overflow
浏览器的同源策略 - Web 安全 | MDN (mozilla.org)
跨源资源共享(CORS) - HTTP | MDN (mozilla.org)
跨域资源共享 CORS 详解
Fetch:跨源请求
九种跨域方式实现原理(完整版)
前端常见跨域解决方案(全)
我把7大跨域解决方法原理画成10张图,做成图解!
值得一看的35个Redis常用问题总结http://www.guosisoft.com/article/detail/243)
国思RDIF低代码快速开发平台(支持vue2、vue3)
一路走来数个年头,感谢RDIF框架的支持者与使用者,大家可以通过下面的地址了解详情。
官方网站:http://www.guosisoft.com/ http://www.rdiframework.net/
特别说明,框架相关的技术文章请以官方网站为准,欢迎大家收藏!
RDIF.vNext低代码快速开发框架由海南国思软件科技有限公司专业团队长期打造、一直在更新、一直在升级,请放心使用!
欢迎关注RDIF.vNext低代码快速开发框架官方公众微信(微信号:guosisoft),及时了解最新动态。
- 上一篇:一文讲透支付宝沙箱的基本应用
- 下一篇:让你的代码认主,只有我自己能调试的代码
相关推荐
- Micheal Nielsen's神经网络学习之二
-
依然是跟着MichaelNielsen的神经网络学习,基于前一篇的学习,已经大概明白了神经网络的基本结构和BP算法,也能通过神经网络训练数字识别功能,之后我试验了一下使用神经网络训练之前的文本分类,...
- CocoaPods + XCTest进行单元测试 c单元测试工具
-
在使用XCTest进行单元测试时,我们经常会遇到一些CocoaPods中的开源框架的调用,比如“Realm”或“Alamofire”在测试的时候,如果配置不当,会导致“frameworknotfo...
- Java基础知识回顾第四篇 java基础讲解
-
1、&和&&的区别作为逻辑运算符:&(不管左边是什么,右边都参与运算),&&(如果左边为false,右边则不参与运算,短路)另外&可作为位运算符...
- 项目中的流程及类似业务的设计模式总结
-
说到业务流程,可能是我做过的项目中涉及业务最多的一个方面了。除了在流程设计之外,在一些考核系统、产业审批、还有很多地方,都用到相似的设计思路,在此一并总结一下。再说到模式,并不是因为流行才用这个词,而...
- 联想三款显示器首批获得 Eyesafe Certified 2.0 认证
-
IT之家7月31日消息,据外媒报道,三款全新联想显示器是全球首批满足EyesafeCertified2.0的设备。据报道,联想获得EyesafeCertified2.0认证的显...
- maven的生命周期,插件介绍(二) 一个典型的maven构建生命周期
-
1.maven生命周期一个完整的项目构建过程通常包括清理、编译、测试、打包、集成测试、验证、部署等步骤,Maven从中抽取了一套完善的、易扩展的生命周期。Maven的生命周期是抽象的,其中的具体任务都...
- 多线程(3)-基于Object的线程等待与唤醒
-
概述在使用synchronized进行线程同步中介绍了依赖对象锁定线程,本篇文章介绍如何依赖对象协调线程。同synchronized悲观锁一样,线程本身不能等待与唤醒,也是需要对象才能完成等待与唤醒的...
- jquery mobile + 百度地图 + phonegap 写的一个"校园助手"的app
-
1jquerymobile+百度地图+phonegap写的一个"校园助手"的app,使用的是基于Flat-UI的jQueryMobile,请参考:https://github.com/...
- Apache 服务启动不了 apache系统服务启动不了
-
{我是新手,从未遇到此问题,请各位大大勿喷}事由:今天早上上班突然发现公司网站出现问题。经过排查,发现是Apache出现问题。首先检查配置文件没有出问题后,启动服务发现Apache服务能启动,但是没法...
- 健康债和技术债都不能欠 公众号: 我是攻城师(woshigcs)
-
在Solr4.4之后,Solr提供了SolrCloud分布式集群的模式,它带来的主要好处是:(1)大数据量下更高的性能(2)更好扩展性(3)更高的可靠性(4)更简单易用什么时候应该使用Sol...
- Eye Experience怎么用?HTC告诉你 eyebeam怎么用
-
IT之家(www.ithome.com):EyeExperience怎么用?HTC告诉你HTC上周除了发布HTCDesireEYE自拍机和HTCRE管状运动相机之外,还发布了一系列新的智能手机...
- Android系统应用隐藏和应用禁止卸载
-
1、应用隐藏与禁用Android设置中的应用管理器提供了一个功能,就是【应用停用】功能,这是针对某些系统应用的。当应用停用之后,应用的图标会被隐藏,但apk还是存在,不会删除,核心接口就是Packag...
- 计算机软件技术分享--赠人玫瑰,手遗余香
-
一、Netty介绍Netty是由JBOSS提供的一个java开源框架。Netty提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。也就是说,Netty...
- Gecco爬虫框架的线程和队列模型 爬虫通用框架
-
简述爬虫在抓取一个页面后一般有两个任务,一个是解析页面内容,一个是将需要继续抓取的url放入队列继续抓取。因此,当爬取的网页很多的情况下,待抓取url的管理也是爬虫框架需要解决的问题。本文主要说的是g...
- 一点感悟(一) 初识 初读感知的意思
-
时间过得很快,在IT业已从业了两年多。人这一辈子到底需要什么,在路边看着人来人往,大部分人脸上都是很匆忙。上海真是一个魔都,它有魅力,有底蕴,但是一个外地人在这里扎根置业,真的是举全家之力,还贷3...
- 一周热门
-
-
Boston Dynamics Founder to Attend the 2024 T-EDGE Conference
-
IDC机房服务器托管可提供的服务
-
详解PostgreSQL 如何获取当前日期时间
-
新版腾讯QQ更新Windows 9.9.7、Mac 6.9.25、Linux 3.2.5版本
-
一文看懂mysql时间函数now()、current_timestamp() 和sysdate()
-
流星蝴蝶剑:76邵氏精华版,强化了流星,消失了蝴蝶
-
PhotoShop通道
-
查看 CAD文件,电脑上又没装AutoCAD?这款CAD快速看图工具能帮你
-
WildBit Viewer 6.13 快速的图像查看器,具有幻灯片播放和编辑功能
-
光与灯具的专业术语 你知多少?
-
- 最近发表
-
- Micheal Nielsen's神经网络学习之二
- CocoaPods + XCTest进行单元测试 c单元测试工具
- Java基础知识回顾第四篇 java基础讲解
- 项目中的流程及类似业务的设计模式总结
- 联想三款显示器首批获得 Eyesafe Certified 2.0 认证
- maven的生命周期,插件介绍(二) 一个典型的maven构建生命周期
- 多线程(3)-基于Object的线程等待与唤醒
- jquery mobile + 百度地图 + phonegap 写的一个"校园助手"的app
- Apache 服务启动不了 apache系统服务启动不了
- 健康债和技术债都不能欠 公众号: 我是攻城师(woshigcs)
- 标签列表
-
- serv-u 破解版 (19)
- huaweiupdateextractor (27)
- thinkphp6下载 (25)
- mysql 时间索引 (31)
- mydisktest_v298 (34)
- sql 日期比较 (26)
- document.appendchild (35)
- 头像打包下载 (61)
- oppoa5专用解锁工具包 (23)
- acmecadconverter_8.52绿色版 (39)
- oracle timestamp比较大小 (28)
- f12019破解 (20)
- np++ (18)
- 魔兽模型 (18)
- java面试宝典2019pdf (17)
- beamoff下载 (17)
- unity shader入门精要pdf (22)
- word文档批量处理大师破解版 (36)
- pk10牛牛 (22)
- server2016安装密钥 (33)
- mysql 昨天的日期 (37)
- 加密与解密第四版pdf (30)
- pcm文件下载 (23)
- jemeter官网 (31)
- iteye (18)