在学习过程中,跨域似乎很简单,无非就是“当一个请求url的协议、域名、端口三者之间任意一个与当前页面url不同即为跨域”;
无非就是“后端加一下跨域的相关响应头即可”。
但是在前后联调过程中,发现跨域并没有那么容易解决。
后端环境是springboot,一开始用的重写WebMvcConfigurer里面的addCorsMappings
,添加相应的响应头,但是发现无效。
百度一下猜测可能是因为过滤器链的顺序问题,因此单独写了一个Filter并将order设为较靠前的位置。
然而,设置的ACCESS_CONTROL_ALLOW_ORIGIN明明是“*”,但是前端得到的却是火狐的一个扩展(具体值我不记得了)。
(后面大概猜测到,由于我用的是火狐插件Rester进行的接口测试,因此origin自带为火狐扩展的专用默认值)
前端要求必须看到“*”号,于是上nginx反向代理,配置参考
但是,前端有多名人员,其中有人成功进行了跨域访问,还有个哥们一直跨域失败。
难道跨域配置在前端还做了什么拦截吗?
经过排查,那位跨域失败的兄弟发现将axios的所有config删除后就能正常访问了。
但是,按理来说,不可能为了跨域就不让用axios的config了,所以应该是其中的某个配置造成的问题,不过具体的原因那个前端没有跟我说,我也不大清楚。
本以为解决了,结果前端很快跟我说,get可以访问,post依旧存在跨域。
一环扣一环,于时又是一波折腾,中间的曲折过程就不说了。
最终发现:
Credentials设为 true之后,Access-Control-Allow-Origin就不能设置为 * 了!
因此需要将Credentials设为false,然后前端就能得到Access-Control-Allow-Origin=*;
而此时,nginx设置的headers信息就需要注释掉,不然存在多重重复头信息,也会报错。
Credentials是用与COOKIE校验的,如果要设为true,则进行如下配置
responce.set(‘Access-Control-Allow-Origin’, request.get(‘origin’));
但是这样,'Access-Control-Allow-Origin’拿到的就不是*,而是每个访问端的专属Origin。
此外,我个人对跨域一直存在误解,比如那个兄弟跟我说/api/sms有跨域限制,于是我用postman\浏览器测试了该接口(项目已经部署在服务器中,我这里的测试也属于远程调用),发现没有跨域问题,而且正常返回了ACCESS_CONTROL_ALLOW_ORIGIN:"*"。
新手大概看不出来这有什么问题,这就是对跨域的误解。
我以为项目部署在远程,地址是121.123.123.8,而我的本机地址是142.11.32.11(瞎写的,突出不一致),于是我就以为这就是跨域访问了,毕竟地址都不一样了。
然而,跨域的含义不是这样,跨域是指“在同一个页面中协议、域名、端口三者存在不一致”,我一直突出协议、域名、端口不一致,忽略了最关键条件“在同一个页面中”,因此凡是单接口请求一般是测不出跨域的,postman、浏览器等其他接口测试工具是没有跨域问题的,他们根本就没有页面差异或者说根本没有页面概念。
前后端之所以有跨域问题,是因为前端也是运行在一个http容器里面的,整过前端项目的都知道,运行项目的时候也是通过localhost:8080进行访问的,这就是“当前页面url”。
如果在其中请求localhost:8081下的一个接口,此时就是“同一个页面中端口不一致”,就发生了跨域。
另外,有大佬说postman的较新版本是模拟前端环境进行测试的,因此跨域也是可以测出来的(也有另一个大佬说postman不存在跨域问题)水平有限,具体的我也没测出来。
更新
本来好好的,结果前端同学依旧发生了跨域,而nginx配置正常。
整了一整天,最终终于解决。
在nginx对OPTIONS请求进行额外处理:
在跨域过程中(PUT、DELETE、发送JSON数据请求),会先发送一次预检OPTIONS请求,然而不知道为什么OPTIONS没有通过上图所示红框的上半部分(印象中OPTIONS似乎为了节省资源,省略了很多东西,所以可能没有执行该部分)
顺便提供一下其他相关资料。
忽略掉OPTIONS请求:
大佬参考的文章
阮一峰跨域详解,强推