跨域与content-type漫谈

1,514 阅读2分钟

1. 背景

会写这个是因为前一段时间在开发的时候和后端联调接口时遇到了一些问题:
  1. 前端是单页面应用
  2. 登录时,鉴权使用的是后端设置的cookie,因此前端开了withCredentials: true 点击此处查看介绍 以便第三方域名能将cookie写进客户端。
  3. 前后端分离之后,为了解决跨域问题,后端在保证接口安全之后在reponse中设置了Access-Control-Allow-Headers:'*'。此时在测试一个post接口时,发现跨域的设置没有生效,浏览器的报错是:Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.

2. 分析以及解决

首先查了MDN的关于 withCredentials 的文档和 HTTP访问控制的文档得到了第一个结论:

1. 第一个结论

**我的post请求发送了预检请求,因为我的request中的 `content-type` 是`application/json`。**在我将`content-type`修改为`application/x-www-form-urlencoded`之后,浏览器不在报错,能够成功地发送请求。

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

当请求满足下述任一条件时,即应首先发送预检请求:

使用了下面任一 HTTP 方法:

  • PUT
  • DELETE
  • CONNECT
  • OPTIONS
  • TRACE
  • PATCH

人为设置了对 CORS 安全的首部字段集合之外的其他首部字段。该集合为:

  • Accept
  • Accept-Language
  • Content-Language
  • Content-Type (需要注意额外的限制)
  • DPR
  • Downlink
  • Save-Data
  • Viewport-Width
  • Width

Content-Type 的值不属于下列之一:

  • application/x-www-form-urlencoded
  • multipart/form-data
  • text/plain

2. 第二个结论

在发送请求的时候,为了验证身份,我在请求内容中都带了 withCredentials : true。在HTTP的响应报文中,是这样的

图中的Access-Control-Allow-Headers位置最开始的时候尝试的是*,但是这样是不可以的,因为MDN中的介绍是:

对于附带身份凭证的请求,服务器不得设置 Access-Control-Allow-Origin 的值为“*”

这是因为请求的首部中携带了 Cookie 信息,如果 Access-Control-Allow-Origin 的值为“*”,请求将会失败。而将 Access-Control-Allow-Origin 的值设置为 http://foo.example,则请求将成功执行。

也就是说,在使用了身份凭证的时候,Access-Control-Allow-Origin必须是一个具体的值。

3. 结论

  1. 首先在Access-Control-Allow-Origin中设置唯一的跨域域名
  2. 对于需要预检的请求,要么使用上文中提到的前端修改content-type的方式来解决,要么后端将前端发送的options请求设置为返回200的状态码,使浏览器能够识别正确的权限,以便能够发送需要预检的请求。