Cors跨域问题,你搞明白了?

理论基础

Cors跨域是什么?Cors的英文全称是Cross-origin Resource Sharing,意思是跨域资源共享。它允许浏览器向非同源服务器发送XMLHttpRequest请求,能够像访问同源本地服务器一般。Cors需要浏览器和服务器同时支持,现在的主流浏览器(Google/IE/FireFox/360)一般都支持跨域的,所以关键在于服务器端实现跨域问题,才能真正实现跨域通信。

跨域请求的分类

跨域分为2种请求:简单请求和非简单请求

1简单请求是什么呢?

同时满足以下两种条件的,就是简单请求

(1) 请求方法是以下三种方法之一:

HEAD

GET

POST

(2)HTTP的头信息不超出以下几种字段:

Accept

Accept-Language

Content-Language

Last-Event-ID

Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain

2非简单请求又是什么呢?

只要一条不满足,就是非简单请求。这是因为浏览器端对于这2种请求是不一样的,详细看阮一峰跨域资源共享CORS详解博客。非简单请求是那种对服务器有特殊要求的请求,比如请求方法是PUT或DELETE,或者Content-Type字段的类型是application/json。非简单请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为"预检"请求(preflight)

Header字段含义

(1)Access-Control-Allow-Origin

它的值要么是请求时Origin字段的值,要么是一个*,表示接受任意域名的请求。关键的是Access-Control-Allow-Origin字段,表示http://api.bob.com可以请求数据。该字段也可以设为星号,表示同意任意跨源请求。如下:Access-Control-Allow-Origin: *

(2)Access-Control-Allow-Methods

该字段必需,它的值是逗号分隔的一个字符串,表明服务器支持的所有跨域请求的方法。注意,返回的是所有支持的方法,而不单是浏览器请求的那个方法。这是为了避免多次"预检"请求。

(3)Access-Control-Allow-Headers

如果浏览器请求包括Access-Control-Request-Headers字段,则Access-Control-Allow-Headers字段是必需的。它也是一个逗号分隔的字符串,表明服务器支持的所有头信息字段,不限于浏览器在"预检"中请求的字段。

(4)Access-Control-Allow-Credentials

它的值是一个布尔值,表示是否允许发送Cookie。默认情况下,Cookie不包括在CORS请求之中。设为true,即表示服务器明确许可,Cookie可以包含在请求中,一起发给服务器。这个值也只能设为true,如果服务器不要浏览器发送Cookie,删除该字段即可。

(5)Access-Control-Max-Age

该字段可选,用来指定本次预检请求的有效期,单位为秒。上面结果中,有效期是20天(1728000秒),即允许缓存该条回应1728000秒(即20天),在此期间,不用发出另一条预检请求。

实战

编写一个过滤器,实现javax.servlet.Filter接口并覆写它的doFilter方法如下

在web.xml文件中,我配置过滤器的规则,所有以/security/开头的做跨域处理。

刚刚开始我也踩了很多坑,没仔细看Header设置了些什么参数,也不知道干啥用。于是出现了一种预检错误。XMLHttpRequest cannot load URL路径地址 Request header field Content-Type is not allowed by Access-Control-Allow-Headers in prelight response。见如下图:

本人英文很菜,原理内功也不深,后来查阮一峰博客,其中有一段话:如果浏览器否定了"预检"请求,会返回一个正常的HTTP回应,但是没有任何CORS相关的头信息字段。这时,浏览器就会认定,服务器不同意预检请求,因此触发一个错误,被XMLHttpRequest对象的onerror回调函数捕获。原因就在这里。因为我这发送的是application/json,它是一个非简单请求,会存在预检,很快发现问题了。

改写前:

改写后:

这不是错误么?明显它是一个逗号分隔的字符串,我怎么写成了*,脑子短路了。

总结

一般来说后台之间相互调用不存在跨域的问题,一旦和前端打交道,准确的说是和浏览器打交道,这个时候就需要解决跨域问题了。解决办法也很多,Cors、JSONP、WebSocket等,但是我更倾向Cors,因为它几乎支持Http的所有请求,JSONP只能用于GET请求。然而后台Cors的解决办法也很多,一种是采用过滤器配置的方式,一种是采用CrossOrigin注解的方式,Spring MVC 4.2开始增加支持跨域访问。我觉得更重要的是要理解它的原理和机制,才能不会掉坑里,比如:Content-Type:application/json它是非简单请求,我之前还真不知道。所以做技术还是要深入,只会用是不够的,阅读源码和原理,多逛大牛技术博客,是非常有益的。这篇流水账先记到这里。

参考资料

1 阮一峰 跨域资源共享 CORS 详解

http://www.ruanyifeng.com/blog/2016/04/cors.html

2 阮一峰 浏览器同源政策及其规避方法

http://www.ruanyifeng.com/blog/2016/04/same-origin-policy.html

3 tianjun2012的专栏 SpringMVC前后台分离,跨域问题的解决方法

https://blog.csdn.net/tianjun2012/article/details/50598551

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20180518G1KGAS00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励