跨域这个事情大家肯定都遇到过,也肯定都清楚是浏览器的同源策略导致的。
我一般遇到了就在网上抄一抄配置就解决了,但是关于跨域到底是怎么个原理一直没有深究,直到遇到了一个有趣的bug:
nginx配置了反向代理后,后端服务依旧403。
围绕着这个bug,来思考2个问题:
- 跨域是前端浏览器的限制,为什么需要后端来进行配置呢?
- 跨域只会发生在浏览器中,使用curl等工具发送请求是不会被跨域限制的,真的是这样吗?服务端是怎么判断请求来源是否是浏览器的?
注:
解决跨域有多种方法,本文只关注CORS
.
浏览器对于简单请求和非简单请求处理的流程不一样,非简单请求会预先使用预检请求(OPTIONS)进行校验,简单请求会直接发送。这部分不是本文关注重点,不影响理解,下文都按照简单请求举例。
为什么需要后端配置?
想要搞清楚这个问题就要了解浏览器对于跨域请求的处理流程。
跨域请求流程图.png
根据以上流程,实际上无论是否同源,后端服务器都会接到请求,所以才会对非简单请求做预校验的优化。
真正判断是否可以跨域的不是浏览器,是后端服务器。后端服务器会在响应头中增加是否允许跨域的响应。
Access-Control-Allow-Origin
浏览器根据响应头中的值来判断是否拦截。
所以是否允许跨域要在后端服务器进行配置。
只有浏览器请求会触发跨域校验吗?
跨域是浏览器的安全策略,那当然应该只有浏览器才会触发了?一开始我也是这么觉得的,但我的后端服务在被nginx反向代理后已经是同源了,却还是返回了403的响应,这是怎么回事呢?
我们就要搞清楚后端服务器是如何判断请求来源是否是浏览器的。
实际上是根据我们的请求头判断的,比如Origin
,Referer
等。具体的判断逻辑是后端服务器自己做,不同的web组件之间不尽相同。
此时我把后端服务器的debug打开,发现后端在校验Origin
时报了不同源的异常,然后直接403返回给nginx,然后再返回到浏览器中。
通过curl验证一下:
curl -w '%{http_code}' -X POST "http://ip:port/auth/login
# 返回值:
{"code":304,"msg":"用户名或密码错误","data":null}200
当我的请求头中不包含Origin
时,可以正常响应。
curl -w '%{http_code}' -H "Origin: http://*.*.*.*" -X POST "http://ip:port/auth/login"
# 返回值:
304
当增加Orgin
时被拦截了。
至此,问题已经捋清楚了,解决方案有很多,我这里直接选择修改nginx,将请求头中的Orgin
置空。
proxy_set_header origin "";
问题解决。