跨域问题研究总结

1、背景前端

 

由于是先后端分离开发,因此跨域问题一直都遇到,但之前一直使用的解决方案是经过代码控制设置response.setheader来解决的,这是在百度搜索获得的最多的一个结果,大部分的文章博客都是用这个方案来解决的,诚然它也一直在起做用,直到我最近开发一个新项目再次遇到跨域问题,明明已经设置了response仍是会发生,我便开始深刻探究,因而就有了一下内容,这是一遍真正解决跨域问题的研究成果,不是百度上人云亦云的复制粘贴来的。spring

 

2、跨域问题的产生json

 

跨域问题在先后端分离开发的场景下常常发生,那么在什么状况下会肯定发生跨域问题呢?就是先后端不一样源的时候,同源须要知足三个条件:后端

1)协议相同 (http  https这种)跨域

2)域名相同tomcat

3)端口相同服务器

 

一般场景下咱们的先后端虽然部署在同一个服务器,但通常都是前端放在NGINX 后端放在tomcat 端口是不一样的,因而就产生了协议相同、域名相同可是端口不一样的跨域问题,此处说个题外话,想要避免跨域问题把先后端都放在同一个tomcat里就好了。app

 

3、CORS的两种请求方式前后端分离

解决跨域有几种方案,通常最经常使用的是CORS,由于此方案不须要前端改动,事实上前面提到的response.setheader的方式也是CORS,本文也只针对CORS进行讲解。url

CORS请求分红两类:简单请求(simple request)和非简单请求(not-so-simple request),只要同时知足如下两大条件,就属于简单请求:

 

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

HEAD

GET

POST

2HTTP的头信息不超出如下几种字段: 

Accept

Accept-Language

Content-Language

Last-Event-ID

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

 

不知足以上条件的就属于非简单请求,我称之为复杂请求。

 

4、两种请求方式的实战解决方案

 

回顾下背景,之前能够解决跨域的方式为什么如今就不起做用了呢?缘由就是出在了这不一样的请求方式上,之前的项目都是简单请求,而如今的项目是复杂请求,解决措施不同了,

 

简单请求

 

简单请求能直接进入到接口里执行它的内部逻辑,执行完正常返回响应,但若是想要响应的结果被正确接收,就须要在响应头里加上跨域的许可Access-Control-Allow,响应头就是Response Header,关于跨域许可的几个参数有:

 

 

 

等等,其中简单请求必需要有的参数是Access-Control-Allow-Origin (容许跨域的源),其余参数请自行查询做用,这里不作开展,所以解决简单请求的跨域的问题只须要在响应头加上 Access-Control-Allow-Origin 参数,例子:

String origin = request.getHeader("Origin");

if (origin == null) {

origin = request.getHeader("Referer");

}

response.setHeader("Access-Control-Allow-Origin",  origin);

response.setHeader("Access-Control-Allow-Credentials",  "true");

response.setHeader("Access-Control-Allow-Methods",  "POST, GET, OPTIONS, DELETE");

response.setHeader("Access-Control-Allow-Headers",  "*");

 

 

复杂请求

 

复杂请求不会直接进入接口内部执行接口的逻辑,而是会先发送OPTIONS 预检请求,若是预检请求不能被正确响应,就不会进入到方法内部执行逻辑,因此处理简单请求在代码里设置响应头的方法就不适用了,请求根本达到不了方法内部,因而咱们须要在执行方法里的代码以前,就处理好响应头,由此不难联想到使用拦截器就能很好的解决问题

复杂请求的处理原理跟简单请求是同样的,只要设置好响应头就能够了,不一样的是在哪一个层设置响应头,固然对于简单请求使用复杂请求的处理方式也一样适用,复杂请求处理的两种方式:

1)使用拦截器,设置好响应头,其中必须的跨域参数仍是Access-Control-Allow-Origin,若是有更改请求头的话也须要带上Access-Control-Allow-Headers(容许的请求头),事实上会引发复杂请求的大部分场景都是由于更改了请求头,发送json数据时须要设置请求头contentType=application/json。 虽然通常的场景只须要这两个参数就够了但仍是建议把全部参数都写全,缘由可看下面。拦截器例子:

public class HeadersCORSFilter

   implements Filter

 {

   public void destroy() {}

   

   public void doFilter(ServletRequest request, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {

   

HttpServletResponse response = (HttpServletResponse)servletResponse;

response.setHeader("Access-Control-Allow-Origin", "*");

response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");

response.setHeader("Access-Control-Max-Age", "3600");

      response.setHeader("Access-Control-Allow-Headers", "*");

      response.setHeader("Access-Control-Allow-Credentials", "true");

      chain.doFilter(request, servletResponse);

   }

   

   public void init(FilterConfig arg0) throws ServletException {

 

System.out.println("拦截器初始化");

}

 }

2)使用@CrossOriginspring能流行的一部分缘由我相信有标签的功劳,使用spring标签能帮助咱们更优雅简洁的编写代码,因此当我了解到有能够解决跨域问题的@CrossOrigin标签时也是坚决果断的用了,按照百度上大多数人的说法在方法上面加上@CrossOrigin标签就能够直接解决问题了,再不济就加上@CrossOriginorigins=”xx”)指明容许跨域的源就能够了,但很遗憾,不管是哪一种方法对我都没有做用,因而深刻研究@CrossOrigin,查看其源码发现,除了allow-methods外其余四个参数都是有默认值的(注:此处是基于org.springframework 4.23版本)

 

 

 

 

 

 

 

 

 

因而尝试增长allow-methods属性:

@CrossOrigin(methods = {RequestMethod.POST})

结果就真的起做用了,完美解决了跨域问题。网上还有些说法是在requestmapping上指定方法@RequestMapping(method = RequestMethod.GET),其原理是同样的,由于@CrossOrigin会默认支持@RequestMapping声明的全部源和方法类型。

 

 

 

5、结论

 

产生跨域问题不要着急,首先分析是否须要规避跨域问题,规避方法就是把先后端放在同一个源里,没法规避的,再分析是简单请求仍是复杂请求,对于复杂请求是否须要规避,规避方法为变复杂请求为简单请求,改变请求方式不使用超出范围的请求头等,没法规避就处理复杂请求。

相关文章
相关标签/搜索