跨域问题
一、原由
最近写接口跟web端的对接。联调过程中出现跨域的问题,着实费了一番功夫,也涉及了以前不知道的东西,以此学习和总结吧。
二、各种解决方案
1.报错信息:
XMLHttpRequestcannot load http://zb.example.com/info.json?ak=E485214565fetch087acde70&level=19&tilex=431625&tiley=198699. The 'Access-Control-Allow-Origin' header contains multiple values'*, *', but only one is allowed. Origin 'http://localhost:63342' is therefore notallowed access.
2.原因分析
浏览器默认不允许跨域访问,包括我们平时ajax也是限制跨域访问的。
产生跨域访问的情况主要是因为请求的发起者与请求的接受者
1、域名不同;
2、端口号不同
3.解决方案
1.JSONP方法
jsonP只能使用GET传递参数,只支持GET方式。即使使用jquery的jsonp方法,type设为POST,也会自动变为GET。我用的是POST,果断pass。
2.iframe
跨域使用POST方式,可以使用创建一个隐藏的iframe来实现,与ajax上传图片原理一样,但这样会比较麻烦。只是看的网上这么说,也不知道具体该如何弄,但是,我找到了另一个更简单的方法了。所以pass。
3.通过设置Access-Control-Allow-Origin来实现跨域访问
如果服务端是 JAVA开发的,添加如下设置允许跨域即可。
response.setHeader("Access-Control-Allow-Origin","*");
添加上这个就表明当前页面可以跨域访问。默认是不允许的。
具体添加的位置有:
(1)可以在过滤器 filter 中的dofilter() 方法设置。
(2)可以在 servlet 的 get 或者post 方法里面设置。
(3)可以放在访问的 jsp页面第一行(response.setHeader("Access-Control-Allow-Origin","*"))。
3.1 举例,我的框架用的是springboot,如果是filter的配置的话,应该这么做:
@Component
public classSimpleCORSFilter implements Filter {
@Override
publicvoid destroy() {
//TODO Auto-generated method stub
}
@Override
publicvoid doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throwsIOException, ServletException {
HttpServletRequestrequest = (HttpServletRequest) req;
HttpServletResponseresponse = (HttpServletResponse) res;
response.setHeader("Access-Control-Allow-Origin",request.getHeader("origin"));
//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","x-requested-with,Cache-Control,Pragma,Content-Type,Token");
response.setHeader("Access-Control-Allow-Credentials","true");
chain.doFilter(req,res);
}
@Override
publicvoid init(FilterConfig arg0) throws ServletException {
//TODO Auto-generated method stub
}
}
@Configuration
public classWebConfig {
@Bean
public FilterRegistrationBeanfilterRegistrationBean(MyFilter myFilter){
FilterRegistrationBeanfilterRegistrationBean = new FilterRegistrationBean();
filterRegistrationBean.setFilter(myFilter);
filterRegistrationBean.setEnabled(true);
filterRegistrationBean.addUrlPatterns("/*");
return filterRegistrationBean;
}
}
如果你的spring版本用的是4以上的,就更简单啦:
@Configuration
public classCorsConfig {
private CorsConfiguration buildConfig() {
CorsConfiguration corsConfiguration =new CorsConfiguration();
corsConfiguration.addAllowedOrigin("*"); // 1
corsConfiguration.addAllowedHeader("*"); // 2
corsConfiguration.addAllowedMethod("*"); // 3
return corsConfiguration;
}
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source= new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", buildConfig()); // 4
return new CorsFilter(source);
}
}
3.2 原理
其实我真的特别好奇,既然浏览器限制跨域访问,但是web开发又特别需要这个功能,为什么一个简单的配置就能够完成呢?不知其然不知其所以然。所以,简单的学习一下原理-CORS协议。
以下均是从各地摘录汇总一起,变成了自己的理解,哈哈,与君共勉:
如今的JS大有一统天下的趋势,浏览器已经成了大多应用最好的安身之所。在寻找跨域解决方案时,发现了最优雅解决方案就是HTML5来带了的“Cross-OriginResource Sharing”的新特性,来赋予开发者权力决定资源是否允许被跨域访问。
CORS是一个W3C标准,全称是”跨域资源共享”(Cross-originresource sharing)。
它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。
整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。
因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口 ,就可以跨源通信。
在服务器响应客户端的时候,带上Access-Control-Allow-Origin头信息(这个header就是让服务器支持CORS的)。
CORS具有以下常见的header
<span style="font-size:14px;">Access-Control-Allow-Origin:http://kbiao.me
Access-Control-Max-Age:3628800
Access-Control-Allow-Methods:GET,PUT, DELETE
Access-Control-Allow-Headers:content-type</span>
<span style="font-size:14px;">
“Access-Control-Allow-Origin”表明它允许”http://kbiao.me“发起跨域请求
“Access-Control-Max-Age”表明在3628800秒内,不需要再发送预检验 请求,可以缓存该结果(上面的资料上我们知道CROS协议中,一个AJAX请求被分成了第一步的 OPTION 预检测请求和正式请求)
“Access-Control-Allow-Methods”表明它允许GET、PUT、DELETE的外域请求
“Access-Control-Allow-Headers”表明它允许跨域请求包含content-type头</span>
刚才的介绍不能算是原理了,只能说是原由了。具体感兴趣的可以查看这篇博客《利用Access-Control-Allow-Origin响应头解决跨域请求》,对于COR协议解释的还不错。
三、原来问题不在这边!!!
说到这里了,其实跨域问题已经解决了。但是在实际的项目中,我们会用到很多东西,也许,阻碍我们的并不是我们认为的。就比如我这次的问题吧,看问题说明“Origin'http://localhost:63342' is therefore not allowedaccess.“一开始就被绕到里面了,到最后实在没办法,全方面排查问题,才发现,原来是我在项目中用nginx做反向代理,这个没问题,问题是我做了两层代理,并且配置文件中都设置了header(解决方案就是,删除其中一个配置文件的header设置)。
在请求数据的时候,header头是这样的:
就可以看出来我设置了两次,并且两次都返回来了。
正常的应该是这样的:
而且我们再仔细看问题的描述:
XMLHttpRequestcannot load http://zb.example.com/info.json?ak=E485214565fetch087acde70&level=19&tilex=431625&tiley=198699. The 'Access-Control-Allow-Origin' header contains multiple values '*, *',but only one is allowed. Origin 'http://localhost:63342' is therefore not allowed access.
我真是。。。
历练吧。
本文转载:https://blog.csdn.net/caozhangyingfei0109/article/details/53439377