本文着重讲怎么处理和分析遇到的跨域问题,对于浏览器同源策略和跨域资源共享只做简要描述。有一定同源策略和跨域知识的程序员可以直接看第三章跨域问题处理方法.
目录:
一、跨域问题的来源
二、跨域请求
三、请求跨域处理方法
一、跨域问题的来源
跨域问题我们只会在浏览器中看到,在服务器、PC客户端、IOS/安卓APP端我们是碰不到跨域问题的.
跨域问题的源头在于浏览器的同源策略,所谓同源策略是指域名,协议,端口相同。当我们违反了浏览器同源策略时,可能就会产生跨域问题。在某个网页下使用ajax访问非同源资源,就会产生跨域问题.
例如:我们的浏览器在访问http://www.czq.com/cross.html页面,访问以下网页:
1.http://www.czq.com/cross.html:8081/policy.html 违反浏览器同源策略,端口不一致,使用ajax访问该资源时产生跨域问题
2.http://www.testczq.com/cross.html/test.html 违反浏览器同源策略,域名不一致,会产生跨域问题
3.http://www.czq.com/test.html 不违反同源策略,协议,域名,端口一致,不会产生跨域问题
同源策略限制的内容很多,不仅限制了对非同源远程资源的访问,也限制了对非同源本地资源(cookie数据等)的访问,我们就不一一详细描述,这不是本文重点.
二、跨域请求
违反浏览器同源策略以后,我们发的这个请求就变成了跨域请求,浏览器会对该跨域请求的响应数据进行跨域允许校验,如果服务器端对跨域做了支持,就可以校验通过,没有跨域错误.
跨域请求分为两类,简单跨域请求和复杂跨域请求.
简单跨域请求的满足要求:
(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
对于这些User-Agent,Pragma,Cache-Control,content-length,host等等这些浏览器自己产生的header头,他们是不会校验的,谁会对自己进行校验呢,当然也不会导致复杂跨域请求的产生.
复杂跨域请求
复杂跨域请求会产生两次http请求,一次预检(options)请求,一次跨域请求(可视为简单跨域请求),服务端需要对这两次请求都做跨域支持,才不会产生跨域问题.
预检请求,主要是提前检测服务器对请求方法(PUT,PATCH,DELET),一些header头是否支持.
预检请求(OPTIONS)内容:
OPTIONS /v2/test HTTP/1.1
Host: www.abc.com
Content-Type: application/x-www-form-urlencoded
Origin: www.test.com
服务器响应内容:
HTTP/1.1 200 OK
Access-Control-Allow-Credentials → true
Access-Control-Allow-Headers → appid,sign,ACCEPT,rpcid,X-REQUESTED-WITH,Content-Type
Access-Control-Allow-Methods → POST,GET,OPTIONS,PUT
Access-Control-Allow-Origin → www.test.com
Connection →keep-alive
Content-Length →0
Date →Thu, 30 May 2019 04:53:57 GMT
对服务器返回header跨域允许字段简单解释
Access-Control-Allow-Credentials,允许跨域请求携带cookie信息(服务器如果不需要cookie,可以填false或者不返回该header字段)
Access-Control-Allow-Headers ,允许跨域请求携带的header字段,如果跨域请求发送了不被服务器允许的字段,浏览器会报跨域错误.
Access-Control-Allow-Methods,允许的跨域请求方法,如果不是服务器允许的请求方法,浏览器会报跨域错误.
Access-Control-Allow-Origin,允许的跨域源,origin是发送请求的源站,返回为*或者具体域名都可以,如果是*,跨域请求则无法携带cookie信息(浏览器不允许)
三、请求跨域处理方法
出现跨域错误的三种可能:
①服务器端没有对跨域进行支持
②前端带了跨域允许外的header头(可以和第一种可能合并)
③由缓存导致的跨域不允许(浏览器本地,cdn,nginx等各地缓存),常见于对静态资源的访问
第一种情况
这个是最容易分析出来的,就是简单的服务器不允许跨域,下面有一段golang的跨域允许代码(其他语言也基本一致)
origin := ctx.Request.Header.Get("Origin")
if origin != "" {
ctx.Header("Access-Control-Allow-Origin", origin)//允许跨域源跨域访问
ctx.Header("Access-Control-Allow-Headers", "appid,sign,Content-Type") //允许跨域的header字段,根据需要自行添加删除
ctx.Header("Access-Control-Allow-Credentials", "true")//允许携带cookie验证信息
ctx.Header("Access-Control-Allow-Methods", "POST,GET,OPTIONS,PUT")// 允许跨域的请求方法
}
第二种情况
该跨域错误是携带了服务器允许之前的header字段,比如
PUT /v2/test HTTP/1.1
Host: www.abc.com
Content-Type: application/x-www-form-urlencoded
Origin: www.test.com
filepath:chen/test.jpg
我们刚刚配置的跨域允许代码Access-Control-Allow-Headers中并没有filepath字段,预检请求时浏览器校验到服务器不支持filepath字段,浏览器则会报不允许跨域错误
此时有两种处理方案:
1.web端删除该字段
2.如果该filepath字段是必须的,则让服务器修改代码,允许该header字段跨域
第三种情况
这种情况是最隐蔽也是最难处理的垮域问题,这种跨域错误常见于对静态资源的访问,例如图片,音频,文档等等.
发生这种错误的前提条件是,两次请求(一次普通请求,一次跨域请求)+缓存机制,示例如下(我们默认服务器对跨域做了充足的支持):
先进行一次普通请求(img标签访问的资源被认为是普通请求)
src="http://www.chen.com/test.jpg"
/>
服务器给的响应信息如下:
HTTP/1.1 200 OK
Content-Length →8888
Date →Thu, 30 May 2019 04:53:57 GMT
<图片信息,二进制数据>
此时文件已经缓存在某个地方了(cdn,浏览器,服务器,nginx等等都有可能)
我们再进行一次跨域请求
GET /test.jpg HTTP/1.1
Host: www.chen.com
Content-Type: application/x-www-form-urlencoded
Origin: www.test.com
我们得到的响应数据如下,此时获取到的是缓存数据,对跨域请求不支持跨域:
HTTP/1.1 200 OK
Content-Length →8888
Date →Thu, 30 May 2019 04:53:57 GMT
<图片信息,二进制数据>
浏览器校验响应数据是否有跨域支持,发现并无跨域支持,报跨域不允许相关错误.这个就是经典的由缓存导致的跨域问题.
第三种情况解决方案:
1.无视缓存出现的位置,前端统一为每个文件url加上时间戳,这是最快的解决方法,同时也是下下策.缓存的不命中会导致文件访问速度变慢,以及加大资源服务器访问压力.
2.如果是浏览器的本地缓存导致的跨域问题(火狐不会导致这个问题,谷歌某些版本会出现这个问题),前端需要审查自身代码,是否对一个文件发出了两种不同的请求(先普通请求,再跨域请求),交换顺序或者去除一种请求都可以.
3.浏览器之外的所有导致跨域问题的缓存,排查哪个环节导致的跨域问题,修改缓存策略,建议对普通请求和跨域请求做出区分.(可以学习火狐浏览器的缓存策略)
————————————————
版权声明:本文为CSDN博主「Star_CSU」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Star_CSU/article/details/90703526