跨域问题

一、原由

 

最近写接口跟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

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

“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头


刚才的介绍不能算是原理了,只能说是原由了。具体感兴趣的可以查看这篇博客《利用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.

 

我真是。。。

历练吧。

 

  • 4
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 10
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值