记一次springMVC的跨域解决方案
日期:2019年5月18日
事情原因:由于微信小程序的开发只有测试环境,而后台提供借口的环境是开发环境;两个环境的域名不同,导致前端开发产生了跨域问题;
理论概念:
1、同源策略:同源策略是浏览器的安全基石,存储在浏览器中的数据(cookie)必须在同域(相同域名)下才能任意的读取,而非同域下的浏览器资源是不允许任意操作的。即同源策略下,非同源的网站之间不能发送ajax请求;
如果没有同源策略,会导致浏览器中的数据可以被任何来源请求进行访问与资源操作,存储在浏览器中的数据就没有任何安全可言;
2、域名:相同域名(www.baidu .com;所有的请求都来自于这个域名下,即为同域下);
不同域名(www.baidu.com,www.google.com;来自google的请求不能直接操作baidu域下的资源);
3、同源:两个页面的【协议】、【端口(如果指定了)】、【主机】都相同,则这两个页面具有相同的源。有一个元素不同,则是不同源的;
4、跨域资源共享(cors:cross-origin resource sharing):1)它是一份浏览器技术的规范,提供web服务从不同域传来沙盒脚本的方法,泳衣避开浏览器的同源策略,jsonp模式的现代版(相关jsonp的知识请自己查阅,本 文不再做解释说明);
2)实现思路是使用自定义的http头部制定一个可以互相认可的约定(例如浏览器给服务器传送一个头信息a,服务器给浏览器传送一个头信息b,就可以不受同源策略 的限制),从而决定请求与响应成功与否;
3)cors不关心安全问题,只解决资源共享的问题;安全性可以从请求时效性,token验证,ip验证,来源验证等方面考虑安全问题;
5、cors与jsonp的区别:1)jsonp只能实现get请求,而cors支持所有的请求;
2)使用cors,前端开发者可以使用普通的xmlhttprequest发起的请求和响应的数据,比jsonp有更好的错误处理;
3)jsonp主要被版本比较老的浏览器支持,这些老版本的浏览器往往不支持cors(ie6,ie7,open mini),而绝大多数现代浏览器都已经支持了cors;
6、注:在跨域请求中,请求是可以发送到服务器的,服务器也可以响应数据,但服务器响应到浏览器的数据,浏览器拒绝解析(不太确定是拒绝接受还是拒绝解析?),导致的资源无法共享;
解决方案:只要解决跨域资源共享即可,cros解决方案主要有以下几种:
1、自定义corsfilter(intercepter):适用于设置单一的(全部)授权访问,所有配置固定,操作简单,不根据请求类型做不同的处理,粒度比较大;
1 @bean 2 public filterregistrationbean corsfilter() { 3 urlbasedcorsconfigurationsource source = new urlbasedcorsconfigurationsource(); 4 corsconfiguration config = new corsconfiguration(); 5 config.setallowcredentials(true); config.addallowedorigin("http://localhost:9000"); 6 config.addallowedorigin("null"); 7 config.addallowedheader("*"); 8 config.addallowedmethod("*"); 9 source.registercorsconfiguration("/**", config); // cors 配置对所有接口都有效 10 filterregistrationbean bean = newfilterregistrationbean(new corsfilter(source)); 11 bean.setorder(0); 12 return bean; 13 }
2、nginx代理配置(配置在location中)
1 # 2 # wide-open cors config for nginx 3 # 4 location / { 5 if ($request_method = 'options') { 6 add_header 'access-control-allow-origin' '*'; 7 add_header 'access-control-allow-methods' 'get, post, options'; 8 # 9 # custom headers and headers various browsers *should* be ok with but aren't 10 # 11 add_header 'access-control-allow-headers' 'dnt,x-customheader,keep-alive,user-agent,x-requested-with,if-modified-since,cache-control,content-type'; 12 # 13 # tell client that this pre-flight info is valid for 20 days 14 # 15 add_header 'access-control-max-age' 1728000; 16 add_header 'content-type' 'text/plain charset=utf-8'; 17 add_header 'content-length' 0; 18 return 204; 19 } 20 if ($request_method = 'post') { 21 add_header 'access-control-allow-origin' '*'; 22 add_header 'access-control-allow-methods' 'get, post, options'; 23 add_header 'access-control-allow-headers' 'dnt,x-customheader,keep-alive,user-agent,x-requested-with,if-modified-since,cache-control,content-type'; 24 } 25 if ($request_method = 'get') { 26 add_header 'access-control-allow-origin' '*'; 27 add_header 'access-control-allow-methods' 'get, post, options'; 28 add_header 'access-control-allow-headers' 'dnt,x-customheader,keep-alive,user-agent,x-requested-with,if-modified-since,cache-control,content-type'; 29 } 30 }
1 #js跨域支持 2 #add_header 'access-control-allow-origin' '*'; 3 #add_header 'access-control-allow-credentials' 'true'; 4 #add_header 'access-control-allow-headers' 'authorization,content-type,accept,origin,user-agent,dnt,cache-control,x-mx-reqtoken,x-requested-with'; 5 #add_header 'access-control-allow-methods' 'get,post,options';
3、使用spring框架提供的注解@crossorigin
1)第一种情况,对接口的粒度进行配置
1 @crossorigin(origins = {"http://localhost:9000", "null"}) 2 @requestmapping(value = "/test", method = requestmethod.get) 3 public string greetings() { 4 return "{\"project\":\"just a test\"}"; 5 }
2)第二种情况,对类的粒度进行配置
1 @crossorigin(origins = {"http://localhost:9000", "null"}) 2 @restcontroller 3 @springbootapplication 4 public class springbootcorstestapplication { 5 // xxx 6 }
4、全局配置
1 @configuration 2 public class webconfig extends webmvcconfigureradapter { 3 4 @override 5 public void addcorsmappings(corsregistry registry) { 6 registry.addmapping("/**") 7 .allowedorigins("http://localhost:9000", "null") 8 .allowedmethods("post", "get", "put", "options", "delete") 9 .maxage(3600) 10 .allowcredentials(true); 11 } 12 }
5、支持多域名配置的corsfilter
因为知道已经有可以用的库可以解决,所以就没重复造*了。其实因为懒,看看别人的源码算了.在搜索cors-filter,目前也就两个可以用
org.ebaysf.web 的 cors-filter,项目地址:
com.thetransactioncompany的 cors-filter,项目地址:
-------------------------------------------------------------------------------------------------
ps:与前端的交互:
非简单请求的跨源请求,浏览器会在真实请求发出前,增加一次 option 请求,称为预检请求( preflight request )。预检请求将真实请求的信息,包括请求方法、自定义头字段、源信息添加到 http 头信息字段中,询问服务器是否允许这样的操作。
比如对于 delete
请求:
options /test http/1.1 origin: http://www.examples.com access-control-request-method: delete access-control-request-headers: x-custom-header host: www.examples.com
与 cors 相关的字段有:
-
access-control-request-method
: 真实请求使用的 http 方法。 -
access-control-request-headers
: 真实请求中包含的自定义头字段。
服务器收到请求时,需要分别对 origin 、 access-control-request-method 、 access-control-request-headers 进行验证,验证通过后,会在返回 http 头信息中添加
access-control-allow-origin: http://www.examples.com access-control-allow-methods: get, post, put, delete access-control-allow-headers: x-custom-header access-control-allow-credentials: true access-control-max-age: 1728000
他们的含义分别是:
- access-control-allow-methods: 真实请求允许的方法
- access-control-allow-headers: 服务器允许使用的字段
- access-control-allow-credentials: 是否允许用户发送、处理 cookie
- access-control-max-age: 预检请求的有效期,单位为秒。有效期内,不会重复发送预检请求
当预检请求通过后,浏览器会发送真实请求到服务器。这就实现了跨源请求
-------------------------------------------------------------------------------------------------
总结:以上提供的几种方案都可以解决跨域问题,视自己业务的实际情况选择方案;个人选择的是最简单的nginx,不对代码进行任何的修改,直接配置在代理中;个人觉得要达到比较细粒度的各种优化,推荐使用第五种方案,但同时会会增加学习成本,对时间比较充足的同学可以研究研究;
参考blog:1、https://www.cnblogs.com/coding-net/p/6207122.html
2、