CORS 跨域详解

CORS 跨域

印象里参考了某位作者的文章,有知道的朋友请告知,谢谢!

跨域产生的原因(浏览器的同源策略)

不同域的客户端脚本不能读写对方的资源

hack 方式跨域

在同域内做一个代理(可以使用nginx代理),JSON-P等。但这些方式都存在缺陷,无法完美的实现跨域读写。

CORS 模型实现跨域

在XMLHttpRequest v2标准下,提出了CORS(Cross Origin Resourse-Sharing)的模型,试图提供安全方便的跨域读写资源。目前主流浏览器均支持CORS。

CORS技术原理

CORS定义了两种跨域请求,简单跨域请求 和 非简单跨域请求。

简单跨域请求:

  • 请求方法为HEAD,GET,POST
  • 请求头只有4个字段,Accept,Accept-Language,Content-Language,Last-Event-ID
  • 如果设置 Content-Type 只能是:application/x-www-form-urlencoded,multipart/form-data,text/plain

其实就是一个白名单。

之所以有这个分类是因为浏览器对简单请求和非简单请求的处理机制是不一样的。当我们需要发送一个跨域请求的时候,浏览器会首先检查这个请求,如果它符合上面所述的简单跨域请求,浏览器就会立刻发送这个请求。如果浏览器检查之后发现这是一个非简单请求,比如请求头含有X-Forwarded-For字段。这时候浏览器不会马上发送这个请求,而是有一个preflight,跟服务器验证的过程。浏览器先发送一个options方法的预检请求。如果预检通过,则发送这个请求,否则就不拒绝发送这个跨域请求。

安全跨域请求的控制方式

非简单请求的预检过程,浏览器先发送一个options方法的请求,带有如下字段:

  • Origin: 普通的HTTP请求也会带有,在CORS中专门作为Origin信息供后端比对,表明来源域。
  • Access-Control-Request-Method: 接下来请求的方法,例如PUT, DELETE等等
  • Access-Control-Request-Headers: 自定义的头部,所有用setRequestHeader方法设置的头部都将会以逗号隔开的形式包含在这个头中

然后如果服务器配置了cors,会返回对应对的字段:

  • Access-Control-Allow-Origin:允许跨域访问的域,可以是一个域的列表,也可以是通配符 * 。这里要注意Origin规则只对域名有效,并不会对子目录有效。即http://foo.example/subdir/ 是无效的。但是不同子域名需要分开设置,这里的规则可以参照同源策略
  • Access-Control-Allow-Credentials: 是否允许请求带有验证信息
  • Access-Control-Expose-Headers: 允许脚本访问的返回头,请求成功后,脚本可以在XMLHttpRequest中访问这些头的信息(貌似webkit没有实现这个)
  • Access-Control-Max-Age: 缓存此次请求的秒数。在这个时间范围内,所有同类型的请求都将不再发送预检请求而是直接使用此次返回的头作为判断依据,非常有用,大幅优化请求次数
  • Access-Control-Allow-Methods: 允许使用的请求方法,以逗号隔开
  • Access-Control-Allow-Headers: 允许自定义的头部,以逗号隔开,大小写不敏感

然后浏览器通过返回结果的这些控制字段来决定是将结果开放给客户端脚本读取还是屏蔽掉。如果服务器没有配置cors,返回结果没有控制字段,浏览器会屏蔽脚本对返回信息的读取。

安全隐患

服务器接收到跨域请求的时候,并没有先验证,而是先处理了请求。所以从某种程度上来说。在支持cors的浏览器上实现跨域的写资源,打破了传统同源策略下不能跨域读写资源。

再一个就是如果程序猿偷懒将Access-Control-Allow-Origin设置为允许来自所有域的跨域请求。那么cors的安全机制几乎就无效了。不过先别高兴的太早。其实这里在设计的时候有一个很好的限制。xmlhttprequest发送的请求需要使用“withCredentials”来带上cookie,如果一个目标域设置成了允许任意域的跨域请求,这个请求又带着cookie的话,这个请求是不合法的。(就是如果需要实现带cookie的跨域请求,需要明确的配置允许来源的域,使用任意域的配置是不合法的)浏览器会屏蔽掉返回的结果。javascript就没法获取返回的数据了。这是cors模型最后一道防线。假如没有这个限制的话,那么javascript就可以获取返回数据中的csrf token,以及各种敏感数据。这个限制极大的降低了cors的风险。

前后端分离后ajax携带cookie请求解决方案

服务器端(http://127.0.0.1):

httpServletResponse.setHeader("Access-Control-Allow-Headers","Origin, X-Requested-With, Content-Type, Accept, User-Agent");
httpServletResponse.setHeader("Access-Control-Allow-Origin","http://127.0.0.1:8000");
httpServletResponse.setHeader("Access-Control-Allow-Credentials","true");

前端(http://127.0.0.1:8000):

$.ajax({
  type:'POST',
  url:'http://127.0.0.1/admin/login',
  dataType:"json",
  data:{},
  xhrFields: {
    withCredentials: true
  },
  success:function(a){},
  error:function(a){}
});

posted on 2018-02-09 12:16  大胡子爸爸  阅读(306)  评论(0)    收藏  举报

导航