跨域是浏览器限制,但是如果服务器设置了 CORS 相关配置,在返回服务器的信息头部会加上 Access-Control-Allow-Origin
,浏览器看到这个字段的值与当前的源匹配,就会解锁跨域限制。
HTTP/1.1 200 OK Date: Sun, 24 Apr 2016 12:43:39 GMT Server: Apache Access-Control-Allow-Origin: http://www.acceptmeplease.com Keep-Alive: timeout=2, max=100 Connection: Keep-Alive Content-Type: application/xml Content-Length: 423
对于 CORS,请求分两种。
符合上面两个条件的都为 CORS 简单请求。简单请求都会直接发到服务器,会造成 CSRF。
不符合简单请求要求的请求都需要先发送预检请求(Preflight Request)。浏览器会在真正请求前发送 OPTION 方法的请求向服务器询问当前源是否符合 CORS 目标,验证通过后才会发送正式请求。
例如使用 application/json 传参的 POST 请求就是非简单请求,会在预检中被拦截。
再例如使用 PUT 方法请求,也会发送预检请求。
上面提到的可以防范 CSRF 的例外,就是指预检请求。即使跨域成功请求预检,但真正请求并不能发出去,这就保证了 CSRF 无法成功。
与同域不同,用于跨域的 CORS 请求默认不发送 Cookie 和 HTTP 认证信息,前后端都要在配置中设定请求时带上 cookie。
这就是为什么在进行 CORS 请求时 axios 需要设置 withCredentials: true
。
下面是 node.js 的后台 koa 框架的 CORS 设置:
/** * CORS middleware * * @param {Object} [options] * - {String|Function(ctx)} origin `Access-Control-Allow-Origin`, default is request Origin header * - {String|Array} allowMethods `Access-Control-Allow-Methods`, default is 'GET,HEAD,PUT,POST,DELETE,PATCH' * - {String|Array} exposeHeaders `Access-Control-Expose-Headers` * - {String|Array} allowHeaders `Access-Control-Allow-Headers` * - {String|Number} maxAge `Access-Control-Max-Age` in seconds * - {Boolean} credentials `Access-Control-Allow-Credentials` * - {Boolean} keepHeadersOnError Add set headers to `err.header` if an error is thrown * @return {Function} cors middleware * @api public */
顺带一提,Access-Control-Allow-Credentials
设为true
时,Access-Control-Allow-Origin
强制不能设为 *
,为了安全,也是挺麻烦啊...