文档章节

CORS 跨域实践

andyqian
 andyqian
发布于 07/18 08:56
字数 2173
阅读 15
收藏 0

本文首发于个人微信公众号《andyqian》,期待你的关注~

前言

  系统通常都是由单体应用逐渐演化而来,演化成为前后端分离的分布式应用。在享受分布式系统带来的诸多好处之时,随之而来的也有不少新的问题。其中跨域问题就成了第一只拦路虎。今天我们就来揭露一下这只老虎的真面目!

什么是跨域?

  在解决问题前,我们首先得先了解什么是跨域?其实我们可以简单的理解跨域就是跨不同的”域名”。但这个域名比我们通常理解中的域名范围更广泛一些。在这里用 “非同源访问” 可能更合适一些。那么同源又是什么呢?

同源为: 相同协议,相同域名,相同端口

早在1995年,同源政策由 Netscape 公司引入浏览器。目前,所有浏览器都实行这个政策。其引入的目的是为了保证用户信息的安全,防止恶意的网站窃取数据。

例如:http://www.andyqian.com  其中:

  1. http 为协议,常用的有http和https协议。

  2. www.andyqian.com为域名。

  3. 端口为80。(默认端口省略)。

     

基于上面的定义。我们来判断一下下面表格中,哪些属于同源,哪些不属于同源。以下述链接为例:

http://www.andyqian.com
URL 是否同源 原因
https://www.andyqian.com 协议不同
http://tech.andyqian.com 域名不同
http://www.andyqian.com:8080 端口
http://www.andyqian.com/a/ 协议,域名,端口均一致

其交互方式,如下图所示:

CORS中的两种请求

  通过上面的介绍,我们已经知道同源的概念,以及跨域是怎么回事。现在我们继续说说,如何解决跨域问题。其实根据同源政策规定,AJAX请求只能发给同源的网址,否则就报错。在日常中,通常我们通过代理服务器以及CORS(Cross-Origin Resource Sharing)跨源资源分享来解决。浏览器中通常将CORS分为简单请求以及复杂请求。

简单请求

  1. 简单请求只支持: GET,POST,PUT请求方法。

  2. 除了用户设置的代理请求头外(Content,User-Agent),仅支持以下请求头:

     
    1. Accept

    2. Accept-Language

    3. Content-Language

    4. Last-Event-ID

    5. Content-type

  3. 其中Content-Type也仅支持以下几种类型:

     
    1. application/x-www-form-urlencoded

    2. multipart/form-data

    3. text/plain

    使用简单请求在发送请求时,浏览器通常会在Request Header中自动添加一个名为Origin的字段,用来表示请求来源。如以下请求信息为例:

     
    1. Accept: application/json, text/javascript, */*; q=0.01

    2. Accept-Encoding: gzip, deflate, br

    3. Accept-Language: zh-CN,zh;q=0.9,en;q=0.8

    4. Connection: keep-alive

    5. Content-Length: 2116

    6. Content-Type: application/x-www-form-urlencoded; charset=UTF-8

    7. Cookie: QC005=7edda8574ca744e800667e65ba85e01d; QP001=1;

    8. Host: apollo.iqiyi.com

    9. Origin: https://www.iqiyi.com

    10. User-Agent: Mozilla/5.0

    其中Origin中的https://www.iqiyi.com 表示请求来源。如果服务端允许该Origin进行访问。其Response Header返回头信息如下:

     
    1. Accept-Charset: utf

    2. Access-Control-Allow-Credentials: true

    3. Access-Control-Allow-Headers: X-Requested-With

    4. Access-Control-Allow-Origin: https://www.iqiyi.com

    5. Connection: keep-alive

    6. Content-Encoding: gzip

    7. Content-Type: application/json;charset=UTF-8

    8. Date: Mon, 16 Jul 2018 15:47:22 GMT

    9. Server: openresty/1.11.2.2

    10. Transfer-Encoding: chunked

    11. Vary: Accept-Encoding

    其中以Access-Control-Allow开头字段均与CORS请求相关。(会在下面章节做详细介绍)。

其交互方式,如图所示:

 

备注: 为了更好的理解。以上例子为爱奇艺登录界面抓包(https://apollo.iqiyi.com/validate)接口的实际Request头信息,Response头信息。有兴趣的童鞋也可以自己抓包看看。

复杂请求:不满足简单请求的,均为复杂请求。

例如:请求方法为DELETE方法。

Content-Type 为 applicatioin/json或者 application/xml

还需要特别注意的是,复杂请求在正式通信之前,浏览器会自动增加一次Request Mehotd方法为: OPTIONS 的HTTP请求,我们通常称之为”预检”请求。

主要用来检查当前请求的Origin以及Header,Method是否能够被服务端允许。

以下列请求例子:

 
  1. Accept: */*

  2. Accept-Encoding: gzip, deflate, br

  3. Accept-Language: zh-CN,zh;q=0.9,en;q=0.

  4. Access-Control-Request-Headers: auhorization,content-type

  5. Access-Control-Request-Method: POST

  6. Connection: keep-alive

  7. Origin: https://www.iqiyi.com

其中:

  1. Access-Control-Request-Headers:表示此次复杂请求额外携带的请求头信息。上例中分别为:auhorization和 content-type

  2. Access-Control-Request-Method: 则表示此次复杂请求的方法。上例为: POST方法。

收到预检请求后。服务端会将Access-Control-Request为前缀的请求头信息进行校验。如果校验通过。则表示服务端允许此次跨域请求。

其返回Response Header信息中则会返回如下信息所示:

 
  1. Access-Control-Allow-Credentials: true

  2. Access-Control-Allow-Headers: *

  3. Access-Control-Allow-Methods: GET, POST, OPTION

  4. Access-Control-Allow-Origin: *

  5. Access-Control-Expose-Headers:authorization

  6. Connection: close

  7. Content-Type: text/plain

则表示服务端允许此次复杂请求。

其交互方式,如下图所示:

CORS中支持所有属性

CORS中服务端可以控制的所有header头属性有:

 
  1. Access-Control-Allow-Origin: *

  2. Access-Control-Allow-Credentials: true

  3. Access-Control-Expose-Headers:authorization

  4. Access-Control-Allow-Headers: *

  5. Access-Control-Allow-Methods: GET, POST, OPTIONS

  6. Access-Control-Max-Age: 1024

其中:

  1. Access-Control-Allow-Origin: (必选参数)表示允许接受请求源。如果为*,则表示接受任意域名的请求。如果已知来源,我们可以设置为特定的值。例如:

    Access-Control-Allow-Origin: https://www.andyqian.com

    指定特定的请求源后,其他请求源的请求过来后均会被拒绝

  2. Access-Control-Allow-Credentials:(可选参数) 表示服务端是否允许携带cookie至服务端。它的值是一个boolean类型的值。为true时则表示服务端明确接收允许携带cookie信

  3. Access-Control-Expose-Headers: (可选参数) 表示请求中的Response Headers中允许返回的自定义Header。如果有多个。则使用符号进行分割。

在CORS请求中,使用XMLHttpRequest对象的getResponseHeader()方法只能拿到以下标准字段:
Cache-Control,Connect,Content-Type,Date,Server,Content-Language,Expires,Last-Modified,Transfer-Encoding,Vary等标准字段。当我们需要使用自定义header头时。我们需要在此处进行填写对应的key值。

例如设置为如下:

Access-Control-Expose-Headers: mysign

我们就可以通过getResponseHeader("mysign")获取到对应的值。

  1. Access-Control-Allow-Headers : (可选参数) 表示允许的Request中的header信息。使用 *表示所有请求头信息。

  2. Access-Control-Allow-Methods: (可选参数) 表示允许跨域的请求方法,如果请求方法不在此列表中。则表示该方法不允许跨域。在校验时,会认为是一个不允许的请求而被拦截。

  3. Access-Control-Max-Age : (可选参数) 表示本次预检请求的有效期,单位为秒。在有效期内,不再发起预检请求。 (建议设置该值,否则每次都会进行一次预检请求)。

如何解决跨域?

  通过上面的了解,我们知道解决跨域问题时,需要在服务端进行配置。如何配置呢?我们以Nginx为例。通常我们在Nginx中的nginx.conf文件中对应location路径下添加如下配置即可:

  1. 跨域名

     
    1. Access-Control-Allow-Origin:   *;

    2. Access-Control-Allow-Credentials: true;

    3. Access-Control-Allow-Methods: POST,PUT,GET,HEAD,OPTIONS

    4. Access-Control-Allow-Headers: *

    5. Access-Control-Max-Age: 21600

    6. Access-Control-Expose-Headers: sign

    上述预检请求的时间有效期为6小时,以及其他对应的值。均可根据实际情况进行修改使用。

  2. 对于OPTIONS预检查请求,我们可以使用下述方法进行返回处理:

     
    1. if ($request_method = OPTIONS ) {

    2.   add_header Content-Length 0;

    3.   add_header Content-Type text/plain;

    4.   return 204;

    5. }

    这样做的好处,是在nginx层做出返回结果。也可以看作是对业务层的一种保护。(状态码不一定为204,只要为成功的状态即可,200也行。)

备注: 建议将内容新建为一个后缀为.conf的文件,在使用时导入即可。

最后

  上面简单记录了什么是跨域以及如何解决跨域。通常我们在开发中,上面这个问题也确确实实的存在。希望在遇到以下问题时,我觉得应该可以优先考虑一下是否是跨域在作祟。

  1. 后端接口在Postman中访问是正常的,在与前端工程师联调时,死活都不通。甚至在测试环境是正常的。一上线到生产环境时。连后端接口请求都访问不了。 (跨域问题)

  2. 后端接口中莫名出现类型转换异常。(OPTIONS方法)

话外音:之前就吃过上面的亏,踩了上面的坑,你们可别再掉下去了。如果已经掉下去了,那就赶紧爬上来。前面还有若干坑等着你呢!(手动微笑)

 

相关阅读:

谈谈用户隐私

Java生成PDF的若干坑

说说Java日志

说说Java注释

这里写图片描述

 扫码关注,一起进步

个人博客: http://www.andyqian.com

© 著作权归作者所有

共有 人打赏支持
andyqian
粉丝 64
博文 37
码字总数 46849
作品 0
长沙
程序员
Jquery+Django 实现 CORS 文件传输

场景描述 遇到这样一个开发场景,服务器A作为一个web server(可以看做master)而服务器B 作为一个运行长时间作业的jobs server(slaver),提供restful API 给服务器A调用,并且接受master的参数...

Allocator
2017/10/18
0
0
ajax跨域,这应该是最全的解决方案了

前言 搬运的历史文章 从刚接触前端开发起,这个词就一直以很高的频率在身边重复出现,一直到现在,已经调试过N个跨域相关的问题了,16年时也整理过一篇相关文章,但是感觉还是差了点什么,于...

dailc
07/01
0
0
SpringBoot 实现前后端分离的跨域访问(CORS)

一、基本介绍 CORS是一种访问机制,英文全称是Cross-Origin Resource Sharing,即我们常说的跨域资源共享,通过在服务器端设置响应头,把发起跨域的原始域名添加到Access-Control-Allow-Orig...

Jokey2017
2017/11/14
0
0
AJAX POST&跨域 解决方案 - CORS

 跨域是我在日常面试中经常会问到的问题,这词在前端界出现的频率不低,主要原因还是由于安全限制(同源策略, 即JavaScript或Cookie只能访问同域下的内容),因为我们在日常的项目开发时会不...

zyt_1978
2016/03/28
64
0
nginx配置反向代理解决前后端分离跨域问题

摘自《AngularJS深度剖析与最佳实践》P132 nginx配置文件如下: server { 把跟路径下的请求转发给前端工具链(如gulp)打开的开发服务器 如果是产品环境,则使用root等指令配置为静态文件服务...

發生了什麽
2016/10/23
1K
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

【七】组合Action

本章描述了常用定义Action的方法。 自定义action builders 我们在action一章已经看过如何声明一个action——有request parameter、无request parameter、有body parser等等。你可以在 asynch...

Landas
29分钟前
0
0
Spring Boot实战之基础回顾

本文作者: 吴伟祥 本文链接: https://wuweixiang.cn/2018/08/21/Spring-Boot实战之基础回顾/ 版权声明: 本博客所有文章除特别声明外均为原创,采用CC BY-NC-SA 4.0 许可协议。转载请在文章开...

吴伟祥
29分钟前
0
0
OAuth认证开发

提示: 以下测试是基于项目安装成功,初始化数据库(initial_db.ddl, oauth.ddl, initial_data.ddl)后的测试, 也可在页面上点击"client_details"菜单里进行测试 方式1:基于浏览器 (grant_type=...

舒文joven
38分钟前
1
0
第二章-对象及变量的并发访问-第二篇

锁对象的改变 请阅读如下代码 public class MainClass { private String lock = "123"; public void printStringB() { try { synchronized (lock) { ......

简心
42分钟前
0
0
日志中记录代理IP以及真实客户端、apache只记录指定URI的日志

apache 日志中记录代理IP以及真实客户端 默认情况下log日志格式为: LogFormat "%h %l %u %t "%r" %>s %b "%{Referer}i" "%{User-Agent}i"" combined 其中%h 是记录访问者的IP,如果在web的前...

李超小牛子
51分钟前
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部