一直都没有时间捣鼓博客,发现自己的描述和总结的习惯都没有了,换新电脑有三个月了,觉得这段时间过得很快,事实上我不更博客都不止三个月了,换电脑是一个原因,其他理由就是毕业季,转正,同事的离职,两个版本的老项目快速迭代,的的确确没有太多时间给自己总结,说白了就是没有时间摸鱼了,晚上有时还要加班,周末都用来睡觉和打游戏虚度,老电脑也不在身边,总之感觉自己过得浑浑噩噩,一边补洞一边自己写出新的烂代码来。对了,另一个不更的原因是CTO成天坐旁边,不明白为啥他总不爱坐自己的办公室😥(好在在写这篇文章的时候他已经换了一个位置坐)
接下来会列一些这么长时间里遇到的比较经典的问题,由于上班节奏变快了,有些忘了记录下来,但之后的日子我还是会继续恢复以前的习惯,常总结,今天梳理一下工作中遇到n次的跨域问题!
第一次遇到跨域还是在去年冬天刚实习的时候,前端大佬说他那调我的http接口报403,捣鼓半天问了组里的大哥找到项目里有应对跨域问题的拦截器,最后问题原因出在前端大佬把访问的路径写岔了,跨域问题也就不了了之。
什么是跨域
在JavaScript中,有一个很重要的安全性限制,被称为“Same-Origin Policy”(同源策略)。这一策略对于JavaScript代码能够访问的页面内容做了很重要的限制,即JavaScript只能访问与包含它的文档在同一域下的内容。
JavaScript这个安全策略在进行多iframe或多窗口编程、以及Ajax编程时显得尤为重要。根据这个策略,在baidu.com下的页面中包含的JavaScript代码,不能访问在google.com域名下的页面内容;甚至不同的子域名之间的页面也不能通过JavaScript代码互相访问。对于Ajax的影响在于,通过XMLHttpRequest实现的Ajax请求,不能向不同的域提交请求,例如,在abc.example.com下的页面,不能向def.example.com提交Ajax请求,等等。
以上来自https://www.cnblogs.com/smiler/p/5829621.html
所谓同源是指,域名,协议,端口相同。当页面在执行一个脚本时会检查访问的资源是否同源,如果非同源,那么在请求数据时,浏览器会在控制台中报一个异常,提示拒绝访问。
这个问题表现上来说与后端并没有什么关系,浏览器并不能在同一个js请求不同域名的接口,否则就会跨域,所以大多数接口需要后端转发,变成同一个域名和端口才能给前端请求,但有的时候明明是同一个项目甚至同一个Controller下的接口也会出现跨域问题,这就不知道前端大佬们到底是如何请求的了,可能是都写在了一块吧。
解决方法
最常用的解决方法是CORS,者是一个W3C标准,全称是”跨域资源共享”(Cross-origin resource sharing),定义了一种浏览器和服务器交互的方式来确定是否允许跨域请求。 它是一个妥协,有更大的灵活性,但比起简单地允许所有这些的要求来说更加安全。
目前我了解到的有两种实现方法,一是加处理响应头的过滤器或拦截器,二是SpringMVC的一个注解@CrossOrigin,第二种方法比较偷懒,我们先来介绍这种偷懒的方法。
@CrossOrigin
在Controller中的方法上添加一个@CrossOrigin注解:
1 |
|
有2个参数:
origins : 允许可访问的域列表
maxAge:准备响应前的缓存持续的最大时间(以秒为单位)。
还可以加上CORS的设置:
1 | "true", allowedHeaders = "*") (allowCredentials = |
这虽然是一个非常简单的处理方法,但有的时候加了还是没有用,看网上有许多分析问题原因究竟出在哪里的帖子,有时要加上CORS配置才生效,有时要制定@RequestMapping是POST还是GET,有时加了这些都没用,把注解搬到类上也没用,感觉有的时候不应该一概而论的解决,要看浏览器究竟报了什么错,根据错误来判断究竟要加什么头或条件,我比较喜欢用土方法,写个过滤器,
Filter + CORS
1 | public class CROSFilter implements Filter { |
这个例子是由于出现了Access-Control-Allow-Headers缺省导致的错误,所以一开始我加了浏览器报错上提示的请求头,”epid,version,web_platform”是某个项目里需要的请求头,缺省就报405,与前端大佬沟通后发现他们请求的时候头就会带这些东西,于是后来遇到类似情况,要改Access-Control-Allow-Headers,我都会优先从请求头里取。
这里再解释一下其他几个CORS相关的参数:
(1)Access-Control-Allow-Origin
该字段是必须的。它的值要么是请求时Origin
字段的值,要么是一个*
,表示接受任意域名的请求。
这个参数的缺省带来的报错非常常见,通常写成*就行,但也遇到写了也没用的情况,那么要具体问题具体分析,看到底缺了什么
(2)Access-Control-Allow-Credentials
该字段可选。它的值是一个布尔值,表示是否允许发送Cookie。默认情况下,Cookie不包括在CORS请求之中。设为true
,即表示服务器明确许可,Cookie可以包含在请求中,一起发给服务器。这个值也只能设为true
,如果服务器不要浏览器发送Cookie,删除该字段即可。
(3)Access-Control-Expose-Headers
该字段可选。CORS请求时,XMLHttpRequest
对象的getResponseHeader()
方法只能拿到6个基本字段:Cache-Control
、Content-Language
、Content-Type
、Expires
、Last-Modified
、Pragma
。如果想拿到其他字段,就必须在Access-Control-Expose-Headers
里面指定。上面的例子指定,getResponseHeader('FooBar')
可以返回FooBar
字段的值。
(4)Access-Control-Allow-Methods
该字段必需,它的值是逗号分隔的一个字符串,表明服务器支持的所有跨域请求的方法。注意,返回的是所有支持的方法,而不单是浏览器请求的那个方法。这是为了避免多次”预检”请求。
前端大佬表示他们在请求时通常都会预检,发一个OPTIONS请求,所以在上面的例子中我也加了这个
(5)Access-Control-Allow-Headers
如果浏览器请求包括Access-Control-Request-Headers
字段,则Access-Control-Allow-Headers
字段是必需的。它也是一个逗号分隔的字符串,表明服务器支持的所有头信息字段,不限于浏览器在”预检”中请求的字段。
(6)Access-Control-Allow-Credentials
该字段与简单请求时的含义相同。
(7)Access-Control-Max-Age
该字段可选,用来指定本次预检请求的有效期,单位为秒。上面结果中,有效期是20天(1728000秒),即允许缓存该条回应1728000秒(即20天),在此期间,不用发出另一条预检请求。
总之,要与前端大佬沟通好是缺省什么,哪个参数报的错,一般他们请求过来什么,我们给他们加什么就行,切忌所有请求一概而论的胡乱加*,当然也许@CrossOrigin也可以达到一样的效果,只是由于我比较笨拙喜欢复杂的写法
WebMvcConfigurer(SpringBoot)
另外,如果你用的是一个SpringBoot项目,不妨试试WebMvcConfigurer,(SpringBoot2.x版本为WebMvcConfigurerAdapter )
建一个Configuration实现WebMvcConfigurer ,老的WebMvcConfigurerAdapter有解决跨域的方法,十分强大
1 | /** 解决跨域问题 **/ |
例如你可以这样解决跨域:
1 |
|
高版本的SpringBoot可以这样实现:
1 |
|