CORS跨域请求
zKing 2018-11-19 HTTP
# 概念
- CORS 是一个 w3c 标准,全称是"跨域资源共享"(Cross-origin resource sharing),但一个请求 url 的协议,域名,端口三者之间任意与当前页面地址不同即为跨域.它允许浏览器向跨源服务器发送 XMLHttpRequest 请求,从而克服 AJAX 只能同源使用的限制.
- 浏览器默认的安全限制为同源策略,即 JavaScript 或 Cookie 只能访问同源(相同协议,相同域名,相同端口)下的内容。但由于跨域访问资源需要,出现了 CORS 机制,这种机制让 web 服务器能跨站访问控制,使跨站数据传输更安全。CORS 需要浏览器和服务器同时支持,目前,主流的浏览器都支持 cors。
- CORS 需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,IE 浏览器不能低于 IE10。
# 分类
# 简单请求
# 最简单的设置
在服务端返回给浏览器的 http header 里,设置
'Access-Control-Allow-Origin':'*';
# 关于 Access-Control
- Access-Control-Allow-Origin:该字段是必须的,其值可能是请求时 Origin 字段的值,也可能是一个*,表示接受任意域名请求。
- Access-Control-Allow-Headers:该字段必须,它是一个都好分割的字符串,表明服务器支持的所有头信息字段
- Access-Control-Expose-Headers:该字段可选。CORS 请求时,XMLHttpRequest 对象的 getResponseHeader()方法只能拿到 6 个基本字段:Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma。如果想拿到其他字段,就必须在 Access-Control-Expose-Headers 里面指定。
- Access-Control-Allow-Credentials:该字段可选,其值类型是布尔型,表示是否允许发送 Cookie。默认情况下 Cookie 不包括在 CORS 请求中。当设为 true 时表示服务器明确许可,Cookie 可以包含在请求中一起发送给服务器。此时需要设置
var xhr = new XMLHttpRequest();
xhr.withCredentials = true;
# 非简单请求
# 预检请求 pre-flight
非简单请求的 CORS 请求,会在正式通信前进行一次 Http 查询请求,又称预检请求。
# 预检请求用的请求方法
- method:OPTIONS,表示这个请求是用来询问的。
- Access-Control-Request-Method:该字段是必须的,用来列出浏览器的 CORS 请求会用到哪些 HTTP 方法。
- Access-Control-Request-Headers:该字段是一个逗号分割的字符串,指定浏览器 CORS 请求会额外发送的头信息字段。
# 预检请求的回应
- Access-Control-Allow-Methods:该字段必须,其值是逗号分隔的一个字符串,表明服务器支持的所有跨域请求的方法。这是为了避免多次预检请求。
- Access-Control-Max-Age:该字段是可选的,用来指定本次预检请求的有效期,单位是秒。Access-Control-Max-Age:20,即允许缓存该条回应 20 秒,再此期间不用发出另一条预检请求
CORS 为非简单请求时,会先进行预检请求再进行下一步的请求,也就是说,在服务端,需要先过滤预检请求,对 request 的 method 进行判断,一旦服务器通过了"预检"请求,以后每次浏览器正常的 CORS 请求,就都跟简单请求一样
# 区分
只要满足以下两大条件,属于简单请求
# 请求方法是以下三种方法之一
- HEAD
- GET
- POST
# HTTP 的头信息不超出以下几种字段
- Accept
- Accept-Language
- Content-Language
- Last-Event-ID
- Content-Type:只限于三个值
- application/x-www-form-urlencoded
- multipart/form-data
- text/plain
# 与 JSONP 的比较
# JSONP 概念
- JSONP 是利用浏览器对 script 的资源引用没有同源限制,通过动态插入一个 script 标签,当资源加载到页面后会立即执行的原理实现跨域的。JSONP 是一种非正式传输协议,该协议的一个要点就是允许用户传递一个 callback 或者开始就定义一个回调方法,参数给服务端,然后服务端返回数据时会将这个 callback 参数作为函数名来包裹住 JSON 数据,这样客户端就可以随意定制自己的函数来自动处理返回数据了。
- JSONP 只支持 GET 请求而不支持 POST 等其它类型的 HTTP 请求,它只支持跨域 HTTP 请求这种情况,不能解决不同域的两个页面之间如何进行 JavaScript 调用的问题,JSONP 的优势在于支持老式浏览器,弊端也比较明显:需要客户端和服务端定制进行开发,服务端返回的数据不能是标准的 Json 数据,而是 callback 包裹的数据。
# 以 jquery 为例,使用 JSONP 的方法
$.ajax({
type: "get",
dataType: "jsonp"
jsonp: "callback"
jsonpCallback:"fn"
url: "",
success:...
...
});
- Ajax 请求需要设置请求类型为 JSONP
- Ajax 请求需要设置回调函数,当前函数值必须与服务器响应包含的 callback 名称相同
- Ajax 请求可以设置 jsonp(可选),传递给请求处理程序或页面,用以获得 jsonp 回调函数名的参数名,默认为:callback
# 服务端返回 Json 数据必须使用 jsonpCallback 设置的值进行包裹
return "fn("content")"
CORS 和 JSONP 都是为了使 web 浏览器能够跨源请求,使用目的相同,但是比 JSONP 更强大。JSONP 只支持 GET 请求,而 CORS 支持所有类型的 HTTP 请求,不过 JSONP 的优势在于支持老式浏览器以及可以向不支持 CORS 的网站跨源请求。
# 总结
浏览器跨域的解决方式有很多种:本文提到的有 jsonp 和 CORS
- jsonp 需要目标服务器配合一个 callback 函数。
- CORS 需要服务器设置 header :Access-Control-Allow-Origin。
另外还有其他的方法为:
- window.name+iframe 需要目标服务器响应 window.name。
- window.location.hash+iframe 同样需要目标服务器作处理。
- html5 的 postMessage+ifrme 这个也是需要目标服务器或者说是目标页面写一个 postMessage,主要侧重于前端通讯。
- nginx 反向代理
个人
nginx 反向代理可以直接转发请求而不用再代码进行其他设置,感觉这种方法会省事很多。 之前有用过网上提供的中转站,直接把接口 api 贴在网址后即可
https://cors-anywhere.herokuapp.com/
关于 nginx 反向代理,请点击 这里