简介
跨域是我在日常面试中经常会问到的问题,这词在前端界出现的频率不低,主要原因还是由于安全限制(同源策略, 即JavaScript或Cookie只能访问同域下的内容),因为我们在日常的项目开发时会不可避免的需要进行跨域操作,所以跨域能力也算是前端工程师的基本功之一。
和大多数跨域的解决方案一样,JSONP也是我的选择,可是某天PM的需求变了,某功能需要改成支持POST,因为传输的数据量比较大,GET形式搞不定。所以折腾了下闻名已久的CORS(跨域资源共享,Cross-Origin Resource Sharing)
CORS能做什么:
正常使用AJAX会需要正常考虑跨域问题,所以伟大的程序员们又折腾出了一系列跨域问题的解决方案,如JSONP、flash、ifame、xhr2等等。
本文介绍的CORS就是一套AJAX跨域问题的解决方案。
CORS的原理:
CORS定义一种跨域访问的机制,可以让AJAX实现跨域访问。CORS 允许一个域上的网络应用向另一个域提交跨域 AJAX 请求。实现此功能非常简单,只需由服务器发送一个响应标头即可。
两种请求
浏览器将CORS请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request)。
只要同时满足以下两大条件,就属于简单请求。
(1) 请求方法是以下三种方法之一: HEAD GET POST (2)HTTP的头信息不超出以下几种字段: Accept Accept-Language Content-Language Last-Event-ID Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain
不是以上内容的就是 非简单请求
简单请求
浏览器会自动带一个Origin头
GET /cors HTTP/1.1 Origin: http://localhost:8080 Host: api.alice.com Accept-Language: en-US Connection: keep-alive User-Agent: Mozilla/5.0...
如果服务器允许这个响应就返回如下内容:
Access-Control-Allow-Origin: http://localhost:8080 Access-Control-Allow-Credentials: true Access-Control-Expose-Headers: myheader Content-Type: text/html; charset=utf-8
非简单请求
首先会触发一个OPTIONS的预检查请求
发送的请求头如下
OPTIONS /ajax.php HTTP/1.1 Origin: http://localhost:8080 Access-Control-Request-Method: PUT Access-Control-Request-Headers: X-Custom-Header Host: localhost:8080 Accept-Language: en-US Connection: keep-alive User-Agent: Mozilla/5.0...
服务端发回的响应:
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://localhost:8080
Access-Control-Allow-Methods: GET, POST, PUT
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
CROS时的请求头和响应头
请求头
请求头 | 含义 |
Origin | 本次请求来自哪个源(协议 + 域名 + 端口)。服务器根据这个值,决定是否同意这次请求 |
Access-Control-Request-Method | 该字段是必须的,用来列出浏览器的CORS请求会用到哪些HTTP方法 |
Access-Control-Request-Headers | 该字段是一个逗号分隔的字符串,指定浏览器CORS请求会额外发送的头信息字段 |
响应头
响应头 | 含义 |
Access-Control-Allow-Origin | 该字段是必须的。它的值要么是请求时Origin字段的值,要么是一个*,表示接受任意域名的请求 |
Access-Control-Allow-Methods | 该字段必需,它的值是逗号分隔的一个字符串,表明服务器支持的所有跨域请求的方法。注意,返回的是所有支持的方法,而不单是浏览器请求的那个方法。这是为了避免多次"预检"请求 |
Access-Control-Allow-Headers | 如果浏览器请求包括Access-Control-Request-Headers字段,则Access-Control-Allow-Headers字段是必需的。它也是一个逗号分隔的字符串,表明服务器支持的所有头信息字段,不限于浏览器在"预检"中请求的字段 |
Access-Control-Allow-Credentials | 它的值是一个布尔值,表示是否允许发送Cookie。默认情况下,Cookie不包括在CORS请求之中。设为true,即表示服务器明确许可,Cookie可以包含在请求中,一起发给服务器 |
Access-Control-Max-Age | 该字段可选,用来指定本次预检请求的有效期,单位为秒。如果填1728000,即允许缓存该条回应1728000秒(即20天),在此期间,不用发出另一条预检请求 |
Access-Control-Expose-Headers | CORS请求时,XMLHttpRequest对象的getResponseHeader()方法只能拿到6个基本字段:Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma。如果想拿到其他字段,就必须在Access-Control-Expose-Headers里面指定 |
使用反向代理服务器可以解决跨域问题
比如将前端代码部署到nginx上,这样访问的地址是localhost:8080/test/aa.html
假设之前访问的跨域地址是 http://10.0.0.123:8080/hello/go
然后前端将 http://10.0.0.123:8080/hello/go 改成 http://localhost:8080/hello/go 这样就是同源了,浏览器就会将session,cookie都带上请求也放行了
之后nginx将访问/hello/go的这个url改写成 http://10.0.0.123:8080,然后nginx去请求这个真实的跨域地址获取数据后返回,这样就解决了跨域问题了
参考