跨域之CORS

什么是跨域?

跨域简单来说就是浏览器禁止加载非同源情况下的脚本。浏览器的同源策略是为了保护用户的隐私安全,如果没有同源策略,很容易遭受网络攻击。

跨域策略限制的内容

  • Cookie、LocalStorage、IndexedDB 等存储性内容
  • DOM 节点
  • AJAX Fetch请求

以下标签允许跨域加载资源:

  1. <img src='' />
  2. <a href=''></a>
  3. <script src=''></script>
  4. <form url=''/>
  5. <iframe url=''/>

什么是同源策略?

所谓同源是指”协议+域名+端口”三者相同,即便两个不同的域名指向同一个ip地址,也非同源。

image.png

常见的跨域场景

image.png

解决跨域的方法–CORS

CORS是跨域资源共享(Cross-origin resource sharing),只要服务端实现了CORS,就可以实现跨域资源共享,服务端通过Access-Control-Allow-Origin属性来实现跨域,该字段表示哪些域名可以访问资源,如果是*就表示所有的域名都可以访问该资源。

它允许浏览器向跨源服务器发出XMLHttpRequest、Fetch请求,从而克服了AJAX只能同源使用的限制。

CORS中,我们将请求分为简单请求和非简单请求,因为浏览器对于这两种请求的处理方式不同。

对于如何、为什么要区分简单/非简单请求,请参照我另一篇文章,对于为什么要区分简单/预检请求的理解

简单请求

当客户端发出简单跨域请求时,会在请求头中自动添加origin字段,表示本次请求来自哪个源(协议 + 域名 + 端口)。服务端根据这个字段看是否允许此次请求。

如果origin指定的源,不在服务器允许的范围内,服务器会返回一个正常的http响应,浏览器发现响应头中没有Access-Control-Allow-Origin字段,就会拦截该响应并报跨域的错误。

如果origin指定的源,在允许的范围内,请求的响应头会返回以下几个字段:

Access-Control-Allow-Origin: http://api.bob.com //必选
Access-Control-Allow-Credentials: true //可选
Access-Control-Expose-Headers: FooBar //可选
复制代码
  • Access-Control-Allow-Origin字段是一定会有的,表示服务端允许访问该资源的源,为*表示允许所有源访问
  • Access-Control-Allow-Credentialstrue表示服务端允许请求携带cookie。如果需要携带cookie的话,浏览器端也要进行设置,设置请求的withCredentials属性为true,这样才能成功在请求中传输cookie。
  • Access-Control-Expose-Headers可选,表示指定XHR请求可通过getResponseHeader()获取到的首部响应字段。

简单跨域请求举例(忽略部分字段):

// 请求头
GET /api HTTP/1.1
Origin:http://localhost.a.com:8080
content-type:'text/plain'
复制代码
// 响应头
HTTP/1.1 200 OK
Access-Control-Allow-Origin:http://localhost.a.com:8080
复制代码

带cookie的简单请求

客户端的准备:

开发者必须在AJAX请求中打开withCredentials属性:表示客户端会携带cookie。

var xhr = new XMLHttpRequest(); xhr.withCredentials = true;

服务端的准备:

Access-Control-Allow-Credentials:true

// 请求头
GET /api HTTP/1.1
Origin:http://localhost.a.com:8080
content-type:'text/plain'
复制代码
// 响应头
HTTP/1.1 200 OK
Access-Control-Allow-Origin:http://localhost.a.com:8080
Access-Control-Allow-Credentials:true //服务器允许携带cookie
复制代码

关于cookie的传输:
如果该请求需要携带cookie,Access-Control-Allow-Origin字段就不能设置为*,有利于保护用户的隐私。并且在携带cookie的时候,只有服务器域名下的cookie才能被携带(详细参考第一方cookie和第三方cookie)。

非简单请求

也就是说浏览器会发送两次http请求。第一次Request Method: OPTIONS,第二次再请求所需内容。

非简单请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为预检请求 (preflight)。

  • 预检请求
// 请求头
OPTIONS /api HTTP/1.1
Origin:http://localhost.a.com:8080
Access-Control-Request-Method:POST
Access-Control-Request-Headers:content-type
复制代码

预检请求时,浏览器会先将此次跨域请求的方法(POST)、额外的请求头部(content-type不是那三种,或者其他的不在简单请求限制中的头部)发送给服务端,服务端查看Origin、Access-Control-Request-Method、Access-Control-Request-Headers字段,来检查是否允许此次请求。

如果服务器不同意此次请求的话,就返回一个正常的http请求,没有任何与CORS相关的字段,浏览器就会认定,服务器不同意预检请求,因此触发一个错误,被XMLHttpRequest对象的onerror回调函数捕获,为跨域错误。

如果同意此次请求的话,就返回以下与CORS相关的字段:

// 响应头
HTTP/1.1 200 OK
Access-Control-Allow-Origin:http://localhost.a.com:8080
Access-Control-Allow-Headers:content-type 
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Max-Age:1728000
复制代码

Access-Control-Allow-Headers表示服务器支持的所有头信息字段,Access-Control-Allow-Methods表示服务器支持的所有请求方法。Access-Control-Max-Age表示预检请求过期的时间。时间之内不用再次进行预检请求。

接下来,就会发正式请求。

返回所有支持的头部信息和方法是为了避免进行多次预检请求。

  • 正式请求
// 请求头
POST /api HTTP/1.1
Origin:http://localhost.a.com:8080
content-type:application/json
复制代码
// 响应头
HTTP/1.1 200 OK
Access-Control-Allow-Origin:http://localhost.a.com:8080
复制代码

简单请求和非简单请求的正式请求是一样的。

带cookie的非简单请求

  • 预检请求
// 请求头
OPTIONS /api HTTP/1.1
Origin:http://localhost.a.com:8080
Access-Control-Request-Method:POST
Access-Control-Request-Headers:content-type
复制代码

预检请求不会携带cookie。

// 响应头
HTTP/1.1 200 OK
Access-Control-Allow-Origin:http://localhost.a.com:8080
Access-Control-Allow-Headers:content-type 
Access-Control-Allow-Credentials:true //仍需该字段
复制代码
  • 正式请求
// 请求头
POST /api HTTP/1.1
Origin:http://localhost.a.com:8080
content-type:application/json
cookie:a=1,b=...
复制代码

开发者必须在AJAX请求中打开withCredentials属性:表示客户端会携带cookie。

var xhr = new XMLHttpRequest(); xhr.withCredentials = true;

// 响应头
HTTP/1.1 200 OK
Access-Control-Allow-Origin:http://localhost.a.com:8080
Access-Control-Allow-Headers:content-type 
Access-Control-Allow-Credentials:true //服务器允许携带cookie
复制代码

参考文章:

juejin.cn/post/684490…
juejin.cn/post/684490…

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享