浅谈先后端分离中的跨资源共享(CORS)


原文地址html


简介

当下不管大厂小厂的先后端开发模式都是先后端分离。之前遇到经过jsonp解决跨域的方式也渐渐的淡出的工程中(不了解jsonp的能够看JSONP跨域请求+简答实现百度搜索)。当前端请求一个接口的时候就会引发跨域,可是当下的前端构建工具都有相应的解决方案,好比webpackweb-dev-server这个插件,就能很简单的启一个本地的服务,而后发请求的时候经过启的本地服务去发送请求,这样就解决跨域问题的一部分了。这种状况下客户端代码和正常非跨域请求一摸同样,不用作任何改变。前端

上面说的方法只是解决了一部分,还有一部分我想你们可能或多或少的会遇到过,就是在登陆场景的时候。服务端的response里面有set-cookie这个字段,在客户端中设置cookie,cookie里面可能包含着的seesionID表示的当前登陆的用户/当前的登陆状态(对这方面不理解的能够看经过cookie和session让http协议变得有状态)。当用户已经登陆而且访问其余页面的时候,服务端会经过cookie中的信息去校验用户登陆状态,若是请求中没有携带身份信息或者身份信息过时(服务端返回401/403)就会跳转到登陆界面。这种状况若是在先后端联调的时候比较麻烦,由于上面方法解决的跨域是不会携带cookie的。目前有两种方法去解决这个:vue

  1. 在登陆以后拿到session/token每一个请求都默认加上这个值(写死在代理中)
  2. 在请求中增长withCredentials,服务端要设置对应的几个响应头,可是对服务端改动比较多。

综上: CORS的主要任务都落在服务端,可是若是为了联调服务端的开发代码和生产代码有区别,他们确定是会不搞的。webpack

背景

今天组里的实习生在使用Axios去验证登陆的时候遇到了跨域的问题(前端是vue, 后端是Spring boot)。 通常的请求经过前端设置代理,服务端设置Access-Control-Allow-origin:\*就能够了,可是登陆时候响应头里面要去set cookie就遇到了问题,结果因为对CORS跨域理解的不是很深入,对预检请求不是很了解,就在axios的issues里面去搜,经过Axios doesn't send cookies with POST and data定位到了问题,原来他后端写的拦截器里面自动把OPTIONS这个请求给过滤掉了,没有让走到后面的流程。ios

什么是CORS

CORS的出现是为了解决因为浏览器的同源策略带来的请求跨域问题。git

“跨资源共享(Cross-Origin Resource Sharing(CORS))是经过HTTP Response header来告诉浏览器,让运行在一个origin(domain)上的web应用被容许访问来自不一样源服务器上指定的资源的一种机制。”github

简单来讲: CORS就是经过设置请求的响应头(能经过开发人员控制的基本都是服务端的响应头,客户端的也会有对应的请求头,但通常不会是开发人员去控制的,后面会仔细说)去控制是否容许某个origin的某个/些请求跨域。web

CORS的功能

CORS标准新增了一组HTTP首部字段,容许服务端声明哪些源站经过浏览器有权访问哪些资源。对于能对服务器产生反作用的HTTP非简单请求(non-simple request)(特别是除了GET请求之外的请求),浏览器必须首先发送一个方法为OPTIONS的一个预检请求(preflight request)来获取服务器是否容许该请求跨域。服务器获得确认以后,才发起真正的HTTP请求。在预检请求中,服务端也能够通知客户端是否要携带Credentials.json

CORS的三种请求

简单请求

某些请求是不会触发预检查请求的,这些请求被成为简单请求(simple request)。 若是一个请求知足下列的全部条件就能够被称为简单请求:axios

  1. 使用下列的方法之一:
  • GET
  • HEAD
  • POST
  1. 除了浏览器自动设置的头,只能设置Fetch 规范容许设置的“CORS安全请求头
  1. Content-Type的值只限于下面几个(注意没有application/json,如今post的请求常用这个,因此当发post请求的时候会触发预检请求不要意外):
  • application/x-www-form-urlencoded
  • multipart/form-data
  • text/plain
  1. 请求中的任意XMLHttpRequestUpload对象均没有注册任什么时候间监听器。XMLHttpRequestUpload对象可使用*XMLHttpRequest.upload *属性访问。
  2. 请求中没有使用ReadableStream对象

预检请求

不知足上面定义的简单请求,都会发送预检请求。

好比浏览器要发送一个POST请求,content-Typeapplication/json,新增一个request header为X-TEST

预检请求的步骤:

  1. 发送一个method为OPTIONS的请求,这个请求的目的是
  • 向服务器请求是否支持实际请求发送的方法(是否支持post方法)
  • 向服务器请求是否支持实际请求新增的header或者不知足简单请求的header
  1. 若是服务器接受并正确返回就发送实际的post,而且能带上相应的header

携带credentials的请求

对于跨域(发生CORS)的请求默认是不会带上凭证信息(credentials)的,若是要发送凭证信息(credentials)就须要设置对应的标识位。

请求:

  • 请求中要设置withCredentials为true。

响应:

  • Access-Control-Allow-Credentials: true
  • Access-Control-Allow-Origin的值再也不是通配符*,应该是单一的origin。

HTTP规范规定Access-Control-Allow-Origin不能是通配符*而且只能是单一的origin。这是由于若是能设置多个的话,证实该服务器就能接受多个域名下面的cookie,这是很危险的。

CORS中的请求头和响应头

响应头

Access-Control-Allow-Origin

Access-Control-Allow-Origin: <origin> | *

origin参数的值制定了容许访问服务器资源的外域URI。对于不须要携带身份凭证的请求,服务器能够指定这个字段的值为通配符*,表示容许来自全部域的请求。

Access-Control-Expose-Headers

该头信息服务器把容许浏览器访问的头放入白名单,例如:

Access-Control-Expose-Headers: X-My-Custom-Header, X-Another-Custom-Header 在跨域访问的时候,XHR对象的getResponseHeader()只能拿到一些最基本的响应头。

Access-Control-Max-Age

指定了预检请求(preflight)请求的结果能被缓存多久(秒为单位)。

Access-Control-Allow-Credentials

当浏览器的credentials设置为true时,是否容许浏览器读取response的内容

Access-Control-Allow-Methods

做为预检请求的响应头,指明了实际请求所容许的HTTP方法。

Access-Control-Allow-Headers

用于预检请求的响应。其指明了实际请求中容许携带的首部字段。 以逗号分割。

请求头

这些字段通常无需手动设置。

Origin

预检请求或实际请求的源站。

不包含任何路径,只是服务器的名称。(不论是否为跨域,这个字段都被发送。)

Access-Control-Request-Method

用于预检请求。其做用是,将实际请求所使用的 HTTP 方法告诉服务器。

参考

相关文章
相关标签/搜索