浏览器和服务器实现跨域(CORS)判定的原理
前端对Cross-Origin Resource Sharing 问题(CORS,中文又称'跨域')应该很熟悉了。众所周知出于安全的考虑,浏览器有个同源策略
,对于不同源的站点之间的相互请求会做限制(跨域限制是浏览器行为,不是服务器行为。)。不过下午想到了一个略无趣的问题:浏览器和服务器到底是如何判定有没有跨域呢?本文主要分两个部分,一是对这个问题的总结,二是nginx下如何配置服务器允许跨域。
<!-- more -->
同源策略
同源指的是域名(或IP),协议,端口都相同,不同源的客户端脚本(javascript、ActionScript)在没明确授权的情况下,不能读写对方的资源。
同源的判定:
以http://www.example.com/dir/page.html
为例,以下表格指出了不同形式的链接是否与其同源:(原因里未申明不同的属性即说明其与例子里的原链接对应的属性相同)
链接 | 结果 | 原因 |
---|---|---|
http:// www.example.com /dir/page2.html |
是 |
同协议同域名同端口 |
http:// www.example.com /dir2/other.html |
是 |
同协议同域名同端口 |
http://user:pwd@ www.example.com /dir2/other.html |
是 |
同协议同域名同端口 |
http://www.example.com: 81 /dir/other.html |
否 | 端口不同 |
https ://www.example.com/dir/other.html |
否 | 协议不同端口不同 |
http:// en.example.com /dir/other.html |
否 | 域名不同 |
http:// example.com /dir/other.html |
否 | 域名不同(要求精确匹配) |
http:// v2.www.example.com /dir/other.html |
否 | 域名不同(要求精确匹配) |
http://www.example.com: 80 /dir/other.html |
不确定 |
取决于浏览器的实现方式 |
是否允许跨域的判定
前文提到了同源策略的判定,然而同源策略在加强了安全的同时,对开发却是极大的不便利。因此开发者们又发明了很多办法来允许数据的跨域传输(常见的办法有JSONP
、CORS
)。当域名不同源的时候,由于跨域实现的存在,浏览器不能直接根据域名来判定跨域限制。那么浏览器具体又是如何实现判定的呢?看下面的例子。
环境说明
参与实验的前端域名三个有:
http://www.zhihu.com
、http://segmentfault.com
、http://localhost
。请求的服务器端地址为
http://localhost/city.json
,服务器解析引擎使用的nginx
,且服务器只配置了允许来自http://segmentfault.com
的跨域请求检测方法:在各个域名下利用chrome浏览器的
console
界面模拟发送ajax请求,代码如下:var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://localhost/city.json',true);
xhr.send();
实验过程
在
http://localhost
域名下,请求成功。服务器回应的http文件头如下:
HTTP/1.1 200 OK
Server: nginx/1.6.2
Date: Sun, 05 Jul 2015 17:44:06 GMT
Content-Type: application/octet-stream
Content-Length: 2084
Last-Modified: Sat, 18 Apr 2015 06:20:12 GMT
Connection: keep-alive
ETag: "5531f79c-824"
Access-Control-Allow-Credentials: true
Access-Control-Allow-Methods: GET, POST, OPTIONS
Accept-Ranges: bytes在
http://segmentfault.com
域名下,请求成功服务器回应的http文件头如下:
HTTP/1.1 200 OK
Server: nginx/1.6.2
Date: Sun, 05 Jul 2015 18:17:27 GMT
Content-Type: application/octet-stream
Content-Length: 2084
Last-Modified: Sat, 18 Apr 2015 06:20:12 GMT
Connection: keep-alive
ETag: "5531f79c-824"
**Access-Control-Allow-origin: http://segmentfault.com**
Access-Control-Allow-Credentials: true
Access-Control-Allow-Methods: GET, POST, OPTIONS
Accept-Ranges: bytes在
http://www.zhihu.com
下,请求失败
虽然都是失败,但是返回的HTTP文件头内容会视服务器是否有配置跨域请求而发生变化
服务器允许跨域请求
(仅允许来自http://segmentfault.com
的跨域请求)
console.log窗口提示:
XMLHttpRequest cannot load http://localhost/city.json. The 'Access-Control-Allow-Origin' header has a value 'http://segmentfault.com' that is not equal to the supplied origin. Origin 'http://www.zhihu.com' is therefore notallowed access.
服务器回应的http文件头如下:
HTTP/1.1 200 OK
Server: nginx/1.6.2
Date: Sun, 05 Jul 2015 17:59:25 GMT
Content-Type: application/octet-stream
Content-Length: 2084
Last-Modified: Sat, 18 Apr 2015 06:20:12 GMT
Connection: keep-alive
ETag: "5531f79c-824"
Access-Control-Allow-origin: http://segmentfault.com
Access-Control-Allow-Credentials: true
Access-Control-Allow-Methods: GET, POST, OPTIONS
Accept-Ranges: bytes
服务器不允许任何跨域请求
console.log窗口提示:
XMLHttpRequest cannot load http://localhost/city.json. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://www.zhihu.com' is therefore not allowed access.
服务器回应的http文件头如下:
HTTP/1.1 200 OK
Server: nginx/1.6.2
Date: Sun, 05 Jul 2015 17:51:29 GMT
Content-Type: application/octet-stream
Content-Length: 2084
Last-Modified: Sat, 18 Apr 2015 06:20:12 GMT
Connection: keep-alive
ETag: "5531f79c-824"
Access-Control-Allow-Credentials: true
Access-Control-Allow-Methods: GET, POST, OPTIONS
Accept-Ranges: bytes
跨域的判定流程
从zhihu
页面的两次浏览器报错以及segmentfault
的成功返回值来看,可以很容易得出浏览器和服务器的合作判定步骤如下:
浏览器先根据同源策略对前端页面和后台交互地址做匹配,若同源,则直接发送数据请求;若不同源,则发送跨域请求。
服务器解析程序收到浏览器跨域请求后,根据自身配置返回对应文件头。若未配置过任何允许跨域,则文件头里不包含
Access-Control-Allow-origin
字段,若配置过域名,则返回Access-Control-Allow-origin
+对应配置规则里的域名的方式
。浏览器根据接受到的http文件头里的
Access-Control-Allow-origin
字段做匹配,若无该字段,说明不允许跨域;若有该字段,则对字段内容和当前域名做比对,如果同源,则说明可以跨域,浏览器发送该请求;若不同源,则说明该域名不可跨域,不发送请求
(但是不能仅仅根据服务器返回的文件头里是否包含Access-Control-Allow-origin
来判断其是否允许跨域,因为服务器端配置多域名跨域的时候,也会出现不能跨域的域名返回包里没有Access-Control-Allow-origin
字段的情况。下文配置说明里会讲。)
配置服务器实现跨域传输
前面讲到了同源策略的基本判定,以及浏览器实现跨域判断的方式,那么,如何在服务器端做配置来允许跨域传输呢?下文将以Nginx为例,讲一下三种情况下的配置。
配置项解析
CORS常用的配置项有以下几个:
Access-Control-Allow-Origin(必含) – 允许的域名,只能填通配符或者单域名
Access-Control-Allow-Methods(必含) – 这允许跨域请求的http方法(常见有
POST
、GET
、OPTIONS
)Access-Control-Allow-Headers(当预请求中包含Access-Control-Request-Headers时必须包含) – 这是对预请求当中Access-Control-Request-Headers的回复,和上面一样是以逗号分隔的列表,可以返回所有支持的头部。
Access-Control-Allow-Credentials(可选) – 该项标志着请求当中是否包含cookies信息,只有一个可选值:true(必为小写)。如果不包含cookies,请略去该项,而不是填写false。这一项与XmlHttpRequest2对象当中的withCredentials属性应保持一致,即withCredentials为true时该项也为true;withCredentials为false时,省略该项不写。反之则导致请求失败。
Access-Control-Max-Age(可选) – 以秒为单位的缓存时间。预请求的的发送并非免费午餐,允许时应当尽可能缓存。
具体配置举例
全域名或者单域名允许跨域
这个最省心
打开Nginx的配置文件(默认为nginx.conf
)。找到对应域名设置的local
配置部分。
添加以下内容:
add_header 'Access-Control-Allow-origin' 'http://www.example.com';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
添加的域名必须带
http://
协议头(否则服务器无法区分是http还是https),如果接受所有域名的跨域请求,则可以用*
(安全性有问题,不推荐)
添加多域名跨域配置
如果允许跨域的域名有多个但出于安全问题又不想配置全域名通配的时候,就可以用到nginx里的if
判断了。
添加如下内容:
if ($http_origin = 'http://segmentfault.com' ) {
add_header 'Access-Control-Allow-Origin' "$http_origin";
}
if ($http_origin = 'http://localhost:4000' ) {
add_header 'Access-Control-Allow-Origin' "$http_origin";
}
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
如果对正则比较熟悉的,可以直接用正则来匹配条件判断,不需要用if这么麻烦的方式。
'Access-Control-Allow-Methods' 允许多参数,'Access-Control-Allow-origin'不允许多参数,所以只能是条件语句判断要不要加这个。这也是我前面提到的为什么即使HTTP文件头返回值里没有'Access-Control-Allow-origin',也不能说明它就是不允许跨域的。
nginx配置文件的
http
配置部分不能用if
条件语句,所以多域名的时候必须加在local
部分内。另外加在local
内的只对对应的服务器域名做跨域请求的配置,加在http
里会让跑在该nginx下的所有网站都统一采取这种配置。Access-Control-Allow-Origin
也可以改成全小写的形式,不影响结果.(access-control-allow-origin
也可以)
浏览器和服务器实现跨域(CORS)判定的原理的更多相关文章
- HTTPS请求HTTP接口被浏览器阻塞,python实现websocket客户端,websocket服务器,跨域问题,dwebsocket,https,拦截,服务端
HTTPS请求HTTP接口被浏览器阻塞,python实现websocket客户端,websocket服务器,跨域问题,dwebsocket,https,拦截,服务端 发表时间:2020-03-05 1 ...
- 跨域CORS
一.跨域CORS是什么 当一个资源从与该资源本身所在的服务器的域或端口不同的域或不同的端口请求一个资源时,浏览器会发起一个跨域 HTTP 请求.出于安全考虑,浏览器会限制从脚本内发起的跨域HTTP请求 ...
- AJAX 跨域 CORS 解决方案
本篇文章由:http://xinpure.com/solutions-for-cross-domain-ajax-cors/ 两种跨域方法 在 Javascript 中跨域访问是比较常见的事情 就像现 ...
- python 全栈开发,Day100(restful 接口,DRF组件,DRF跨域(cors组件))
昨日内容回顾 1. 为什么要做前后端分离? - 前后端交给不同的人来编写,职责划分明确.方便快速开发 - 针对pc,手机,ipad,微信,支付宝... 使用同一个接口 2. 简述http协议? - 基 ...
- zuul+security跨域Cors问题解决
zuul+security跨域Cors问题解决 简介 场景 在服务后台都会出现跨域cors问题,不过一般spring解决起来比较方便,在框架+框架的基础上,问题就显得特别明显了,各种冲突,不了解源码的 ...
- 谷歌、火狐浏览器下实现JS跨域iframe高度自适应的完美解决方法,跨域调用JS不再是难题!
谷歌.火狐浏览器下实现JS跨域iframe高度自适应的解决方法 导读:今天开发的时候遇到个iframe自适应高度的问题,相信大家对这个不陌生,但是一般我们都是在同一个项目使用iframe嵌套页面,这个 ...
- netCore2.0 Api 跨域(Cors)
1.在使用netCore2.0 使用WebApi的过程中涉及到了跨域处理. 在Microsoft.AspNetCore.All包中包含跨域Cors的处理,不必单独添加. 2.打开Startup.cs文 ...
- 浏览器同源策略,跨域请求jsonp
浏览器的同源策略 浏览器安全的基石是"同源政策"(same-origin policy) 含义: 1995年,同源政策由 Netscape 公司引入浏览器.目前,所有浏览器都实行这 ...
- IIS Manager 配置文件修该,允许跨域CORS访问
IIS Manager 配置文件修该,允许跨域CORS访问 IIS Manager 的api访问会出现跨域问题,需要 IIS Manager的配置文件中修改. 配置文件的路径:C:\Program F ...
随机推荐
- 【ASP.NET Core快速入门】(七)WebHost的配置、 IHostEnvironment和 IApplicationLifetime介绍、dotnet watch run 和attach到进程调试
WebHost的配置 我们用vs2017新建一个空网站HelloCore 这里的CreateDefaultBuilde实际上已经在内部替我们做好了默认配置. UseKestrel 使用kestrel ...
- DocX开源WORD操作组件的学习系列四
DocX学习系列 DocX开源WORD操作组件的学习系列一 : http://www.cnblogs.com/zhaojiedi1992/p/zhaojiedi_sharp_001_docx1.htm ...
- cache2go源码最后一讲 - examples
先看一下我们讲到哪里了: cache2go的源码前面我们已经讲完了cacheitem和cachetable的实现,今天cahce和examples会一起讲完~ 1.cache.go源码 ...
- RDIFramework.NET ━ .NET快速信息化系统开发框架 V3.2->Web版本“产品管理”事例编辑界面新增KindEditor复文本编辑控件
KindEditor是一套开源的HTML可视化编辑器,主要用于让用户在网站上获得所见即所得编辑效果,兼容IE.Firefox.Chrome.Safari.Opera等主流浏览器.KindEditor使 ...
- Kubernetes系列02—Kubernetes设计架构和设计理念
本文收录在容器技术学习系列文章总目录 1.Kubernetes设计架构 Kubernetes集群包含有节点代理kubelet和Master组件(APIs, scheduler, etc),一切都基于分 ...
- 痞子衡嵌入式:ARM Cortex-M内核那些事(2)- 第一款微控制器
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是第一款Cortex-M微控制器. 1.天生荣耀:ARM Cortex-M处理器由来 ARM公司自2004年推出ARMv7内核架构时,摒弃 ...
- SpringBoot基础系列-SpringBoot配置
原创作品,可以转载,但是请标注出处地址:https://www.cnblogs.com/V1haoge/p/9990680.html SpringBoot基础系列-SpringBoot配置 概述 属性 ...
- Shell编程(week4_day3)--技术流ken
本节内容 1. shell流程控制 2. for语句 3. while语句 4. break和continue语句 5. case语句 6. shell编程高级实战 shell流程控制 流程控制是改变 ...
- Spring Cloud Alibaba基础教程:Nacos配置的加载规则详解
前情回顾: <Spring Cloud Alibaba基础教程:使用Nacos实现服务注册与发现> <Spring Cloud Alibaba基础教程:支持的几种服务消费方式(Res ...
- 【转载】Windows Server 2012服务器删除IIS方法
在Windows Server2012版本的服务器系统中,我们可以通过服务器管理器中的"添加角色和功能"来添加IIS的Web服务器,当我们不再使用IIS功能时候,我们也可以通过删除 ...