美文网首页
跨域——前端 er 需要知道的 CORS

跨域——前端 er 需要知道的 CORS

作者: 去冲浪鸭 | 来源:发表于2019-12-10 02:09 被阅读0次

作为一个前端开发工程师,每天都要对接端口,如果你对跨域知识不了解,不知道什么是 CORS,那么我敢肯定你会经常遇到一些下面图片中的报错,对于如何解决却摸不着头脑,本文就从 CORS 入手介绍一下「跨域」的前世今生,让你面对跨域问题时再也不用发愁。


image.png

本篇文章主要分为两个部分:

  • 跨域的原理
  • CORS 如何调试

为什么会有跨域问题出现?

根本原因:浏览器通过同源策略来防止恶意网站窃取数据

这里我们需要了解这些前提:

什么是同源策略

同源是指:协议、域名、端口都相同
非同源的资源之间不能相互通信

同源政策的目的

是为了保证用户信息的安全,防止恶意的网站窃取数据。

同源策略限制了什么

如果非同源,共有三种行为受到限制

  • Cookie、LocalStorage 和 IndexDB 无法读取
  • DOM 无法获得
  • AJAX 请求不能发送

矛盾冲突:不同源之间的合理通信也会受到限制

虽然这些限制是必要的,但是有时很不方便,合理的用途也受到影响。

解决方案:CORS、代理 和其他几种解决方案

下面详细介绍CORS和代理两种方式(最通用,最贴合我们的业务场景)

方案一:CORS

现在项目研发一般都是前后端分离的模式,前后端代码在上线的时候也是分开部署的,这个时候就肯定会涉及到跨域问题,CORS 是一种简洁,简单,标准的跨域请求方式,要发出 CORS 请求,首先我们要先了解这些前置知识:

前置知识

什么是 CORS

CORS是一种W3C标准,它允许服务器通过一些自定义的头部来限制哪些源可以访问它自身的资源。

CORS还规定对于一些可能对服务器数据产生副作用的http请求,需要在正式请求被发出之前,先发送一个options的预检请求,获知服务器是否允许该跨源请求。

注意:跨域并非是浏览器限制了发起请求,也可能是跨域请求其实可以发出去,但是返回结果被浏览器拦截了

非简单请求正式发送前,浏览器会帮忙发出一个预检请求

上面说了,CORS还规定对于一些可能对服务器数据产生副作用的http请求,需要在正式请求被发出之前,先发送一个options的预检请求,获知服务器是否允许该跨源请求。

预请求 发生在下列情况中:

  • 使用GET或POST以外的方法;
  • 利用POST发送application/x-www-form-urlencoded, multipart/form-data, or text/plain之外的Content-Type;例如,post body的Content-type为application/xml
  • 发送自定义的头信息,如x-pagination-count

操作方式

其实,实现 CORS 通信的关键在于 服务器。

CORS需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,IE浏览器不能低于IE10。

整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。

因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。

那么服务端应该怎么做呢?

服务器需要在响应头部中配置一些与跨源有关的字段

Access-Control-Allow-Origin
origin 参数的值指定了允许访问该资源的外域 URI。
对于不需要携带身份凭证的请求,服务器可以指定该字段的值为通配符*,表示允许来自所有域的请求

Access-Control-Expose-Headers
在跨域访问时,XMLHttpRequest对象的getResponseHeader()方法只能拿到一些最基本的响应头,Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma,如果要访问其他头,则需要服务器设置本响应头
例如前后端协作时,分页信息要储存在header头部,需要设置 x-pagination-count

Access-Control-Allow-Headers
指明了实际请求中允许携带的首部字段

Access-Control-Allow-Credentials
指定了当浏览器的credentials设置为true时是否允许浏览器读取response的内容。
当用在对preflight预检测请求的响应中时,它指定了实际的请求是否可以使用credentials。

请注意:简单 GET 请求不会被预检;如果对此类请求的响应中不包含该字段,这个响应将被忽略掉,并且浏览器也不会将相应内容返回给网页。

Access-Control-Max-Age
指定了preflight请求的结果能够被缓存多久(多少秒)

Access-Control-Allow-Methods
指明了实际请求所允许使用的 HTTP 方法

Access-Control-Allow-Headers 和 Access-Control-Expose-Headers 的区别

access-control-allow-headers:实际request请求中可以带上哪些头部

access-control-expose-headers:指的是浏览器发出 the actual request 得到 response, 浏览器可以使用/读取哪些 response 中的 headers

注意: cookie 始终遵循同源策略

需要注意的是,如果要发送Cookie,Access-Control-Allow-Origin就不能设为*,必须指定明确的、与请求网页一致的域名。同时,Cookie依然遵循同源政策,只有用服务器域名设置的Cookie才会上传,其他域名的Cookie并不会上传,且(跨源)原网页代码中的document.cookie也无法读取服务器域名下的Cookie。

方案二:代理

这种方案可以用于解决开发环境的跨域问题,如果你的系统上线部署时是前后端同源部署,生产环境不会出现跨域问题,那就可以用这种方案来解决开发调试的问题。

这种方式是成本最小(不需要服务端的参与,前端可以自己解决)的一种解决跨域的方案。

综上,这个跨域方案可以基于以下两个条件来使用:

  • 生产环境,前后端同源部署
  • 后端还没有配置CORS,你就可以自己撸起袖子上,不用block在别人那边

原理

代理的原理是开发时客户端这边启动一个服务端来拦截客户端发出去的请求,该服务端代为发送请求至真实的服务端,这样通信就是发生在服务端和服务端之间,也就不会出现所谓的跨域问题。

操作方式

webpack 文档

其他方案

在 CORS 规范出来之前,还有一些其他的跨域解决方案(不推荐使用)

  • document.domain + iframe
  • 动态创建script
  • location.hash + iframe
  • window.name + iframe
  • postMessage
  • JSONP
  • web sockets

其实这些解决方案在CORS这个标准出现之后就比较少有人用了,我也没有真正使用过这几个方案,这里不过多讨论,感兴趣的可以参考 这篇文章 看下

CORS 的调试

分析headers

大部分的 CORS 错误都可以归因与请求头和响应头的不匹配。

所以,CORS 出现错误,分析的第一个动作就是分析 headers,大部分的 CORS 错误都可以归因与请求头和响应头的不匹配,例如:

image

请求头中定义了 Access-Control-Request-Method: POST 以及 Access-Control-Request-Headers: authorization,content-type, 但是服务端没有与之匹配的响应头,所以报错。

辅助工具

浏览器的开发者工具

需要关注 console 和 network 两个面板,这个是我们最常用的方法,以后可以收集一些常见报错放上来,现在不多赘述。

抓包工具

简单请求(没有预检请求的那种)浏览器是不会给你展示具体的请求信息(响应头和请求头),这个时候你要怎么查看头部信息来定位问题呢?可以使用网络抓包工具,常用的有 Wireshark或Fiddler。

(使用教程网上很多,可以自行搜索查看)

curl
要用curl来模拟跨域请求(其实关键是模拟options请求),我们需要先来观察一下options请求的特征:

  • 具有OPTIONS的HTTP方法
  • Origin标头
  • Access-Control-Request-Method标头。

所以要使用curl模拟预检请求,我们需要模仿这三个特征。
(同样的道理也适用于postman,反正关键是你要设定正确的请求头才能模拟出 options请求)

curl --verbose -H "Origin: http://localhost:9090" -H "Access-Control-Request-Method: POST" -X OPTIONS http://xxx.xxx.xxx:8080/a/b

下图是curl的全部内容:


image

反正这里的请求结果里,如果响应的状态码不是200,那你就大胆地去和后端 battle 吧(当然前提是你已经正确设置了请求头,模拟出来的确实是options请求)

总结

无论使用什么辅助工具,其实总结起来就是三步:

  1. 关注请求和响应头
  2. 比较headers以查看是否存在不匹配的
  3. 解决问题
    1. 更新客户端的 request headers 以发送正确的请求
    2. 更新服务器的 response headers 以允许客户端来请求

相关文章

  • 跨域——前端 er 需要知道的 CORS

    作为一个前端开发工程师,每天都要对接端口,如果你对跨域知识不了解,不知道什么是 CORS,那么我敢肯定你会经常遇到...

  • 前端跨域

    CORS跨域 1.CORS跨域-服务端设置,前端直接调用说明:后台允许前端某个站点进行访问 2.JSONP跨域-前...

  • django跨域配置

    前言——跨域请求 前端对Cross-Origin Resource Sharing 问题(CORS,中文又称'跨域...

  • 深入跨域问题(3) - 利用 JSONP 解决跨域

    深入跨域问题(1) - 初识 CORS 跨域资源共享;深入跨域问题(2) - 利用 CORS 解决跨域深入跨域问题...

  • 深入跨域问题(2) - 利用 CORS 解决跨域

    阅读目录: 深入跨域问题(1) - 初识 CORS 跨域资源共享;深入跨域问题(2) - 利用 CORS 解决跨域...

  • 跨域访问

    参考文献:jsonp解决跨域问题 . cors解决跨域问题 . (java+前端小白)第一次碰到跨域问题,小伙伴们...

  • 深入跨域问题(4) - 利用代解解决跨域

    阅读目录 深入跨域问题(1) - 初识 CORS 跨域资源共享;深入跨域问题(2) - 利用 CORS 解决跨域深...

  • 跨域问题详解分析

    参考文档 CORS详解 跨域资源共享 CORS 详解 js中几种实用的跨域方法原理详解 跨域的那些事儿 跨域与跨域...

  • koa2解决跨域请求和options请求

    跨域 使用koa-cors解决跨域问题 添加一个中间件 解决options请求 由于做了跨域,所以前端用post请...

  • Nginx跨域

    Nginx解决跨域问题(CORS) CORS(Cross-Origin Resource Sharing) 跨域资...

网友评论

      本文标题:跨域——前端 er 需要知道的 CORS

      本文链接:https://www.haomeiwen.com/subject/idlwgctx.html