HTTP学习笔记(三):HTTP跨域机制
2018-02-27 23:48
239 查看
概述
当一个资源从与该资源本身所在的服务器不同的域或端口请求一个资源时,资源会发起一个跨域 HTTP 请求。出于安全原因,浏览器限制从脚本内发起的跨源HTTP请求。 例如,XMLHttpRequest和Fetch API遵循同源策略。 这意味着使用这些API的Web应用程序只能从加载应用程序的同一个域请求HTTP资源。如果要支持处于不同域名下的页面向服务器发起请求,就需要应用一些特殊的首部字段和技术来实现,这就叫作“跨域”
简单请求
首先先谈一下对于一个跨域请求,什么叫做简单请求。简单请求是指同时满足如下三个条件的请求:
1. 请求的方法为GET,HEAD,或者POST
2. 请求的首部没有人为设置如下字段以外的字段:Accept,Accept-Language,Content-Language,Content-Type(此字段还有第三个条件限制)
3. Content-Type的值不为如下三个值以外的值:application/x-www-form-urlencoded,multipart/form-data,text/plain
若为满足上述条件的简单跨域请求的话,浏览器在向服务器发送请求时,只需在请求头部附加一个字段:Origin,该字段表示的是发起请求的页面的域。比如说,如果我从example.com/test这个页面发起一个跨域请求,那么Origin的值就为example.com。
服务器在接受到这个简单跨域请求后,如果允许该Origin表示的域名访问的话,会在响应报文中加上一个字段:Access-Control-Allow-Origin。该字段的值表示允许请求该资源的外域。如果这个资源允许任何外域进行访问的话,就可以将该字段的值设置为*。
预检请求(Preflight)
与上述的简单请求不同,预检请求表示的是在对资源发起实际的请求之前,会先向服务器发送一个请求进行预先检查,检查服务器是否允许该跨域请求的请求。所有不满足前文所述的简单请求的三个条件之一的请求都是预检请求。预检请求在发起实际的跨域请求之前,会首先向服务器发送一个请求方法为OPTIONS的请求。在这个请求的头部,会附带上如下字段中的部分或全部:
Origin(一定存在)
Access-Control-Request-Method:表示要向服务器申请在接下来的跨域请求中允许使用的方法名
Access-Control-Request-Headers:表示要向服务器申请在接下来的跨域请求中允许使用的头部字段
接下来用一个例子具体说明这些字段的使用,假如在example.com页面存在如下的一个HTTP跨域请求:
var invocation = new XMLHttpRequest(); var url = 'http://bar.other/resources/post-here/'; var body = '<?xml version="1.0"?><person><name>Arun</name></person>'; function callOtherDomain(){ if(invocation) { invocation.open('POST', url, true); invocation.setRequestHeader('X-PINGOTHER', 'pingpong'); invocation.setRequestHeader('Content-Type', 'application/xml'); invocation.onreadystatechange = handler; invocation.send(body); } }
如上代码所述,在HTTP首部人为设置了一个自定义字段X-PINGOTHER,且Content-Type的值是application/xml,不满足简单请求的条件三,故是一个预检请求,这个请求的预检HTTP请求报文大致可能长这样:
OPTIONS /resources/post-here HTTP/1.1 Host:bar.other Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: en-us,en;q=0.5 Accept-Encoding: gzip,deflate Accept-Charset: ISO-8859-1,utf-8; a38e q=0.7,*;q=0.7 Connection: keep-alive Origin:example.com Access-Control-Request-Methods: POST Access-Control-Request-Headers: X-PINGOTHER, Content-Type
可见,虽然POST方法是简单请求可使用的方法,但是在该预检请求中还是设置了Access-Control-Request-Method字段为POST。然后在Access-Control-Request-Headers中设置了两个不满足简单请求条件三的头部字段的名称。
服务器在接受到上述报文后,会检查其中的两个Access相关的字段值,如果允许该跨域请求的话,就会在响应报文中加入如下的字段的部分或全部:
Access-Control-Allow-Origin:同简单请求
Access-Control-Allow-Methods:表示允许客户端在跨域请求中使用的方法名
Access-Control-Allow-Headers:表示允许客户端在跨域请求中使用的头部字段值
Access-Control-Max-Age:表示允许客户端缓存本次跨域请求的最大有效时间,单位为秒,这句话的意思是在该值表示的时间内,客户端再次发起跨域请求都不需要再发送预检请求了。
Access-Control-Expose-Headers:在跨域请求中,客户端如果使用XHR的getResponseHeaders,默认只能得到如下的字段值:Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma,如果使用了该字段的话,就能指示客户端能够获取哪些字段的值。
上述请求的响应报文大致长这样:
HTTP/1.1 200 OK Content-Language:en-us Content-Encoding:gzip Content-Length:0 Content-Type:text/plain Access-Control-Allow-Origin:example.com Access-Control-Allow-Methods:GET,OPTIONS,POST Access-Control-Allow-Headers:X-PINGOTHER,Content-Type Access-Control-Max-Age: 3600 Access-Control-Expose-Headers:X-PINGOTHER
带凭证的跨域请求
HTTP默认是不允许跨域请求携带凭证,比如Cookie、authorization等。如果要允许这一行为,我们需要设置XMLHttpRequest对象的withCredentials属性为true。这样,在发送跨域请求时,浏览器就会在请求报文上附带上设置好的凭证信息。如果服务器允许这一行为的话,要在响应报文中带上如下字段:Access-Control-Allow-Credentials:true
设置为其他值都代表不允许携带凭证,不允许携带凭证的话,浏览器在接受到响应报文时,不会将响应报文的实体内容返回给客户端,而是会忽略掉。(注意这个true是区分大小写的,写成True也不行)
同时,如果接受到了带凭证的跨域请求的话,服务器返回的响应报文中,Access-Control-Allow-Origin的值不可设置为通配符*,而是一定要设置为具体的允许访问的外域名称。
相关文章推荐
- HTTP学习笔记(二):HTTP重定向机制
- Nginx 学习笔记(八)http和https跨域问题解决
- JavaScript权威设计--跨域,XMLHttpRequest(简要学习笔记十九)
- ASP.NET 3.5核心编程学习笔记(26):HttpApplication对象
- Ubuntu启动流程学习笔记(Upstart事件机制)
- ASP.NET MVC学习笔记-MVC运行机制之源码剖析
- WCF学习笔记3(客户端内部运行机制分析)
- 黑马程序员_多线程的创建和运行机制学习笔记
- Linux编程学习笔记--proc文件系统 http://www.cnblogs.com/weichsel/archive/2012/06/23/2559613.html
- mfc学习笔记之如何修改mfc消息机制处理顺序
- HTTP学习笔记
- Android Activity和Intent机制学习笔记
- 关于跨域-学习笔记
- android内核剖析学习笔记:AMS(ActivityManagerService)内部原理和工作机制
- http原理学习笔记
- Linux 第六周学习笔记 (2),特殊权限列表,at延时任务及定时机制,系统临时文件的管理
- NGUI学习笔记 - 通过UIEventlistener和UIbutton来学习NGUI的消息机制
- android个人学习笔记-触摸事件机制
- HTTP学习笔记1-基本定义
- node.js 基础学习笔记3 -http