关于CORS 应该注意的几点
前言
对于跨域,随着w3c的CORS的出现,相比较于有些年头的jsonp,CORS以其简单安全,支持post的优势越来越收到大家的欢迎。具体如何CORS的原理和实现,直接推荐阮老师的文章,十分详细。本文主要关注CORS实现过程中的几个疑惑点。
预检请求
背景
浏览器将CORS请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request)。
简单请求
同时满足一下条件的即是简单请求:
- 请求方法是以下三种方法之一:
HEAD、GET、POST - HTTP的头信息不超出以下几种字段
Accept
Accept-Language
Content-Language
Last-Event-ID
Content-Type:只限于三个值application/x-www-form、multipart/form-data、text/plain
非简单请求
显然,不同时满足则为非简单请求(可以认为是复杂请求)。两者的差别在于复杂请求在与服务端交互时多了一次options的预检请求,毕竟复杂请求一般就是HTTP请求头信息超出限制或者method为put、delete等操作行为,处于安全考虑,需要服务端先行验证来决定是否给予相关权限。
如下所示(示例来自阮老师文章):
var url = 'http://api.alice.com/cors';
var xhr = new XMLHttpRequest();
// PUT method为复杂请求,要预检
xhr.open('PUT', url, true);
xhr.setRequestHeader('X-Custom-Header', 'value');
xhr.send();
非简单请求,浏览器自动发送otpios的预检请求,请求头如下:
OPTIONS /cors HTTP/1.1
// 请求源
Origin: http://api.bob.com
// 必须字段,指明正式cors请求将会使用那些method
Access-Control-Request-Method: PUT
// 除简单头之外,额外的请求头
Access-Control-Request-Headers: X-Custom-Header
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...
对于预检信息,服务端一般做了如下操作:
1、检查origin、Access-Control-Request-Method和Access-Control-Request-Headers等字段,确认是否允许跨域,如果允许跨域作出回应:
HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:39 GMT
Server: Apache/2.0.61 (Unix)
// 允许的源
Access-Control-Allow-Origin: http://api.bob.com
// 允许的请求方式
Access-Control-Allow-Methods: GET, POST, PUT
// 允许额外header
Access-Control-Allow-Headers: X-Custom-Header
Content-Type: text/html; charset=utf-8
Content-Encoding: gzip
Content-Length: 0
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain
如果不允许跨域,依然响应该请求,不过不携带CORS相关的信息。浏览器则会认为服务器不允许跨域,触发错误。
// 常见的跨域错误
XMLHttpRequest cannot load http://api.alice.com.
Origin http://api.bob.com is not allowed by Access-Control-Allow-Origin.
到这里一个流程结束,不过我们要关注的是options 预检请求之后 code 返回的问题
options 成功之后,返回code 200 还是 204
常规预检的就是对于options的请求直接返回code 200的响应,表示校验通过。
但是前两天发现有的返回为code204。两者之间的差别具体在哪呢。
常见用法
1、针对特定接口支持CORS时,在代码里加判断对于options返回200
// 随便找了段java代码
if (req.getMethod().equals("OPTIONS")) {
res.setStatus(200);
}
2、如果整个域名都支持CORS,可以再nginx侧直接配置,此时常见的是返回204.
if ($request_method = 'OPTIONS') {
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods GET,POST,PUT,DELETE,OPTIONS;
#****省略...
return 204;
}
总结
两者之间的差别,首先可以参考下204和200 对应的含义(下面内容摘自MDN)。
200
请求成功,成功的具体含义依据http method 的不同而有所差别。:
- GET: 资源已经被提取并在消息中文中传递
- POST: 描述动作结果的资源在消息体中传输
204
服务器成功处理了请求,但不需要返回任何实体内容,并且希望返回更新了的元信息。
客户端是浏览器的haul,用户浏览器应保留发送了该请求的页面,而不产生任何文档视图上的变化。由于204响应被禁止包含任何消息体,因此它始终以消息头后的第一个空行结尾。
简单总结,204返回表示请求成功,并且无消息体,优势在于节省网络请求。
具体到options请求,选用哪一个。
贴切的来说,应该像其他options请求一样为预检optiosn请求返回相同的code状态码,相关规范不要求或者推荐其他内容。
fecth请求
例如对于Fetch 规范 要求CORS协议的status可以为200-209里面的任意值。
If a CORS check for request and response returns success
and response’s status is an ok status,
run these substeps.
如果response为一个okstatus就可以继续执行
An ok status is any status in the range 200 to 299, inclusive.
并不要求具体哪一个值。
所以从fetch来看,两者均可选择。
HTTP 1.1
对于http/1.1 规范来说,有一章节专门定义了各种响应code。对于2开头的2-XXcode,分别描述如下:
- 200
请求成功,成功的具体含义依据http method 的不同而有所差别。 - GET: 资源已经被提取并在消息中文中传递
- POST: 描述动作结果的资源在消息体中传输
- OPTIONS: communications options成功的表示
由上可知,对于options预检请求的响应,需要包含下面两种情况:
1、表明请求成功
2、描述通信选项(这里包括, Access-Control-Allow-Methods 和 Access-Control-Allow-Headers这些响应头)
看起来,上面就是200在http定义中的含义,显然满足,但是如果继续看204的含义,好像也可以满足需求。
204
服务器成功处理了请求,但不需要返回任何实体内容,并且希望返回更新了的元信息。
客户端是浏览器的话,用户浏览器应保留发送了该请求的页面,而不产生任何文档视图上的变化。由于204响应被禁止包含任何消息体,因此它始终以消息头后的第一个空行结尾。
结论
首先两者都可以使用,对于200,从定义而言更符合场景和定义。但是204无消息体,优势在于节省网络请求。
至于用哪个,大家自行做下判断。
跨域 读取cookie
作为常见的场景,cookie一般会存放一些,鉴权会话等信息。对于CORS跨域,默认的是不包含cookie的。
A cross-origin request by default does not bring any credentials (cookies or HTTP authentication)
如果要操作cookie需要分别从服务端和客户端两个场景来看。
客户端 request 携带cookie
request如果要携带cookie,需要特定参数指明。可能看到过这个参数为credentials或者withCredentials,什么时候用两者呢。主要跟请求的实现有关:
Fetch 使用credentials
直接使用原生Fetch的话,需要设置credentials。credentials 是Request接口的只读属性,用于表示用户代理是否应该在跨域请求的情况下从其他域发送cookies。这与XHR的withCredentials 标志相似,不同的是有三个可选值(后者是两个):
- omit: 从不发送cookies.
- same-origin: 只有当URL与响应脚本同源才发送 cookies、 HTTP Basic authentication 等验证信息.(浏览器默认值,在旧版本浏览器,例如safari 11依旧是omit,safari 12已更改)
- include: 不论是不是跨域的请求,总是发送请求资源域在本地的 cookies、 HTTP Basic authentication 等验证信息.
CORS跨域的时候,只需要如下设置:
fetch('http://another.com', {
credentials: "include"
});
- XHR 使用withCredentials
基于XMLHttpRequest实现的请求使用withCredentials来允许携带cookie。
该属性为boolean类型,所以只有true/false两个取值,默认为false。
这样也很好理解,默认不携带是处于安全考虑。
使用如下
var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://example.com/', true);
xhr.withCredentials = true;
xhr.send(null);
适用框架:jquery的ajax,axios等。
服务端 Access-Control-Allow-Credentials
当客户端设置了允许携带cookie之后,并不能完成该操作,毕竟是跨域,服务端也需要做响应设置,否则浏览器拿不到正确响应。
Access-Control-Allow-Credentials:true
看MDN 的解释:
The Access-Control-Allow-Credentials response header tells browsers whether to expose the response to frontend JavaScript code when the request's credentials mode (Request.credentials) is "include".
当 credentials为include的时候,通知浏览器是否将响应暴露给前端jscode,如果为false,js不能读取响应自然请求报错。
只有Access-Control-Allow-Credentials为true时,才会将响应暴露给客户端。
当作为预检请求响应头时,表明该实际请求(即后面的真正请求)是否可以使用credentials。
不过对于简单请求,因为没有预检,如果服务端没有正确响应,浏览器会忽略该属性,并不会直接报错。
需要与XMLHttpRequest.withCredentials属性或者Fetch 的credentials 配合使用。
注意
如果要发送Cookie,Access-Control-Allow-Origin就不能设为星号,必须指定明确的、与请求网页一致的域名。
同时,Cookie依然遵循同源政策,只有用服务器域名设置的Cookie才会上传,其他域名的Cookie并不会上传。
且(跨源)原网页代码中的document.cookie也无法读取服务器域名下的Cookie。
毕竟cookie是有path来保证封闭性的,如果可以随便读取不管从安全还是性能上都是一种隐患。
多域名跨域
对于多域名跨域,方法比较多。
1、Access-Control-Allow-Origin:*
允许任意域名跨域,显然支持多域名。不过从安全性和cookie的使用的角度来看并不推荐。
2、动态匹配域名
这种实现方式比较多,原理就是声明允许的多域名配置,可以是数组或者是正则,根据当前请求的域名,来判断是否在适用返回内,在的话则设置Access-Control-Allow-Origin为当前域名。
具体实现这里就不写了。
结束语
参考文章
http://www.ruanyifeng.com/blog/2016/04/cors.html
https://fetch.spec.whatwg.org/#cors-protocol-and-credentials
http://www.yunweipai.com/archives/9381.html
以上是在工作中偶然发现的几点疑惑,解决之后深究了下具体原理。希望能对其他同学有所帮助,抛砖引玉,一起努力。
关于CORS 应该注意的几点的更多相关文章
- CORS详解[译]
介绍 由于同源策略的缘故,以往我们跨域请求,会使用诸如JSON-P(不安全)或者代理(设置代理和维护繁琐)的方式.而跨源资源共享(Cross-Origin Resource Sharing)是一个W3 ...
- ASP.NET Web API 跨域访问(CORS)
一.客户端用JSONP请求数据 如果你想用JSONP来获得跨域的数据,WebAPI本身是不支持javascript的callback的,它返回的JSON是这样的: {"YourSignatu ...
- ASP.NET Core CORS 简单使用
CORS 全称"跨域资源共享"(Cross-origin resource sharing). 跨域就是不同域之间进行数据访问,比如 a.sample.com 访问 b.sampl ...
- jQuery.Ajax IE8 无效(CORS)
今天在开发的时候,遇到一个问题,$.get()在 IE8 浏览器不起作用,但 Chrome,Firefox 却是可以的,网上资料很多,最后发现是 IE8 默认不支持 CORS 请求,需要手动开启下: ...
- CORS简介
现在请跟我做:在您的浏览器的地址栏中输入www.yhd.com并敲击回车.在网站内容全部加载完毕后,按F12打开浏览器的调试窗口.当切换到Sources页时,您会发现您当前所看到的一号店的页面是从多个 ...
- AJAX POST&跨域 解决方案 - CORS
一晃又到新年了,于是开始着手好好整理下自己的文档,顺便把一些自认为有意义的放在博客上,记录成点的点滴. 跨域是我在日常面试中经常会问到的问题,这词在前端界出现的频率不低,主要原因还是 ...
- IIS 启用CORS ,IISExpress 通过IP 访问
在IIS 10中启用CORS: <system.webServer> <handlers> <remove name="OPTIONSVerbHandler ...
- CORS基础要点:关于dataType、contentType、withCredentials
事实上,面试时我喜欢问跨域,因为多数开发者都知道它并且常用,而我希望能从面试者的回答中知道他在这个问题的深入程度,进一步看看面试者研究问题的思维方式及钻研精神,然而确实难到了很多人,当然这也不是面试通 ...
- CORS详解
介绍 由于同源策略的缘故,以往我们跨域请求,会使用诸如JSON-P(不安全)或者代理(设置代理和维护繁琐)的方式.而跨源资源共享(Cross-Origin Resource Sharing)是一个W3 ...
- C#进阶系列——WebApi 跨域问题解决方案:CORS
前言:上篇总结了下WebApi的接口测试工具的使用,这篇接着来看看WebAPI的另一个常见问题:跨域问题.本篇主要从实例的角度分享下CORS解决跨域问题一些细节. WebApi系列文章 C#进阶系列— ...
随机推荐
- 维纳过程(Wiener Process)与高斯过程(Gaussian Process)
维纳过程又叫布朗运动过程(Brownian motion): 1. 维纳过程 维纳过程 Wt 由如下性质所描述: W0=1, a.s.(a.s.,almost surely)
- C#或者WPF中让某个窗体置顶
原文:C#或者WPF中让某个窗体置顶 前记:在工作中有个需求,要求不管到那个界面,我必须让一个浮动条(其实是个窗体)置顶. 我用wpf,因为有之前有好几个界面已经设置成topmost了,所以在这几个界 ...
- MySQL SYS CPU高的案例分析(二)
原文:MySQL SYS CPU高的案例分析(二) 后面又做了补充测试,增加了每秒context switch的监控,以及SQL执行时各步骤消耗时间的监控. [测试现象一] 启用1000个并发线程的压 ...
- Mac OS X通过结合80port
Mac OS X 由于要绑定80port须要ROOT权限, 可是假设用root权限启动eclipse或tomcat又会造成, 启动创建的各类文件是root的,普通用户无法删除. 为此. 我们能够通过p ...
- STM32 模拟I2C (STM32F051)
/** ****************************************************************************** * @file i2c simu. ...
- WPF ListView的使用
<Window x:Class="XamlTest.Window14" xmlns="http://schemas.microsoft.com/win ...
- 一步一步造个IoC轮子(一):IoC是什么
一步一步造个Ioc轮子目录 一步一步造个IoC轮子(一):IoC是什么 一步一步造个IoC轮子(二):详解泛型工厂 一步一步造个IoC轮子(三):构造基本的IoC容器 前言 .net core正式版前 ...
- JQuery 判断checkbox是否选中,checkbox全选,获取checkbox选中值
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- Win8 Metro(C#)数字图像处理--2.67图像最大值滤波器
原文:Win8 Metro(C#)数字图像处理--2.67图像最大值滤波器 [函数名称] 最大值滤波器WriteableBitmap MaxFilterProcess(WriteableBi ...
- GIS基础软件及操作(十二)
原文 GIS基础软件及操作(十二) 练习十二. ArcMap制图-地图版面设计 设置地图符号-各种渲染方式的使用 使用ArcMap Layout(布局)界面制作专题地图 将各种地图元素添加到地图版面中 ...