前后端分离对接坑点记录

作者: veaxen 分类: 未分类 发布时间: 2019-02-19 18:33

前言

现在很多项目都是采用前后端分离的模式来进行开发的,这样做有几个好处:

  1. 实现解耦,前后端分工明确,各司其职;
  2. 可以同时水平扩展前后端服务器;
  3. 减少后端服务器的并发/负载均衡压力;
  4. 提高开发效率

除了以上几点,前后端分离还有其他优势,这里就不细说了,但是前后端分离会导致跨域问题。跨域问题的根本原因:应为浏览器受到同源策略的限制,当前域名的js只能读取同域下的窗口属性。什么是同源策略?就是不同的域名,不同端口,不同的协议不允许共享资源,这是为了保障浏览器安全。同源策略是针对浏览器设置的门槛。

这里需要注意的是,只有访问类型为xhr(XMLHttpRequest)的才会出现跨域。

跨域问题的解决方案

  • 修改浏览器的设置
  • 修改请求的方式:jsonp
  • CORS

jsonp是js前端发起请求的时候,利用只有xhr请求才会出现跨域的问题的特点,让服务端不在返回json数据,而是返回一段js代码,将json的数据以参数的形式传递到函数中,所以需要服务端配合。

把重点放在CORS。

CORS解决跨域

首先要知道什么是CORS。

跨域资源共享(CORS) 是一种机制,它使用额外的 HTTP 头来告诉浏览器 让运行在一个 origin (domain) 上的Web应用被准许访问来自不同源服务器上的指定的资源。当一个资源从与该资源本身所在的服务器不同的域、协议或端口请求一个资源时,资源会发起一个跨域 HTTP 请求。

比如,站点 http://domain-a.com 的某 HTML 页面通过 的 src 请求 http://domain-b.com/image.jpg。 网络上的许多页面都会加载来自不同域的CSS样式表,图像和脚本等资源。

出于安全原因,浏览器限制从脚本内发起的跨源HTTP请求。 例如,XMLHttpRequest和Fetch API遵循同源策略。 这意味着使用这些API的Web应用程序只能从加载应用程序的同一个域请求HTTP资源,除非响应报文包含了正确CORS响应头。

我们知道跨域访问的请求头中存在Origin的字段,用来记录当前的发起访问的域名,我们可以在服务端增加一个响应头Access-Control-Allow-Origin来告诉浏览器我们支持它的获取就可以了,这个可以在服务端代码里实现,也可以通过nginx反向代理实现,这里我推荐用nginx:

add_header Access-Control-Allow-Origin *;

*代表允许任何域名。

但是仅仅加这一句只能解决简单请求的跨域问题,简单请求满足一下几个条件:

  1. 使用下列方法之一
    • GET
    • HEAD
    • POST
  2. Fetch 规范定义了对 CORS 安全的首部字段集合,不得人为设置该集合之外的其他首部字段。该集合为:
    • Accept
    • Accept-Language
    • Content-Language
    • Content-Type (需要注意额外的限制)
    • DPR
    • Downlink
    • Save-Data
    • Viewport-Width
    • Width
  3. Content-Type 的值仅限于下列三者之一:
    • text/plain
    • multipart/form-data
    • application/x-www-form-urlencoded
  4. 请求中的任意XMLHttpRequestUpload 对象均没有注册任何事件监听器;
    • XMLHttpRequestUpload 对象可以使用 XMLHttpRequest.upload 属性访问。
  5. 请求中没有使用 ReadableStream 对象。

如果请求与简单请求不同,必须首先使用 OPTIONS 方法发起一个预检请求到服务器,以获知服务器是否允许该实际请求。”预检请求“的使用,可以避免跨域请求对服务器的用户数据产生未预期的影响。

预检请求中会携带两个头部信息:

  • Access-Control-Request-Method: POST
  • Access-Control-Request-Headers: X-PINGOTHER, Content-Type

字段 Access-Control-Request-Method 告知服务器,实际请求将使用 POST 方法。字段 Access-Control-Request-Headers 告知服务器,实际请求将携带两个自定义请求首部字段:X-PINGOTHER 与 Content-Type。服务器据此决定,该实际请求是否被允许。

对此,我们需要对 OPTIONS 方法进行处理,并返回:

  • Access-Control-Allow-Origin: http://foo.example
  • Access-Control-Allow-Methods: POST, GET, OPTIONS
  • Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
  • Access-Control-Max-Age: 86400

首部字段 Access-Control-Allow-Methods 表明服务器允许客户端使用 POST, GET 和 OPTIONS 方法发起请求。
首部字段 Access-Control-Allow-Headers 表明服务器允许请求中携带字段 X-PINGOTHER 与 Content-Type。与 Access-Control-Allow-Methods 一样,Access-Control-Allow-Headers 的值为逗号分割的列表。
最后,首部字段 Access-Control-Max-Age 表明该响应的有效时间为 86400 秒,也就是 24 小时。在有效时间内,浏览器无须为同一请求再次发起预检请求。请注意,浏览器自身维护了一个最大有效时间,如果该首部字段的值超过了最大有效时间,将不会生效。

如果我们的请求需要携带Cookie信息,响应头还需要 Access-Control-Allow-Credentials: true 否则浏览器将不会把相应内容返回个请求的发送者。

这里有个需要特别注意的时,如果需要携带Cookie,那么服务器不得设置 Access-Control-Allow-Origin 的值为“*”。 这是因为请求的首部中携带了 Cookie 信息,如果 Access-Control-Allow-Origin 的值为“*”,请求将会失败。而将 Access-Control-Allow-Origin 的值设置为 http://foo.example, 则请求将成功执行。

最后,给出我简单的nginx配置:

    location /api {
        add_header Access-Control-Allow-Headers     Content-Type,Client-Type,Client-Ver,Client-Chan,Range;
        add_header Access-Control-Allow-Credentials true;
        add_header Access-Control-Allow-Origin      $http_origin;
        add_header Access-Control-Max-Age           86400;
        add_header Access-Control-Allow-Methods     GET,POST,OPTIONS,DELETE;

        if ($request_method = "OPTIONS") {
            return 204;
        }
        proxy_pass http://backend;
    }

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!

发表评论

电子邮件地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据