ajax跨域问题以及解决方案

在工作中,大家应该都遇到过ajax跨域问题,浏览器的错误如下:

XMLHttpRequest cannot load http://目标地址No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://当前页面地址' is therefore not allowed access.

为什么会出现跨域问题

跨域,指的是浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对JavaScript施加的安全限制。

在此说明一下,所谓的同源,指的是域名、协议、端口均相等。举例如下:

http://www.abc.com/a/b 调用 http://www.abc.com/d/c(非跨域)

http://www.abc.com/a/b 调用 http://www.def.com/d/c (跨域:域名不一致)

http://www.abc.com:81/a/b 调用 http://www.abc.com:82/d/c (跨域:端口不一致)

http://www.abc.com/a/b 调用 https://www.abc.com/d/c (跨域:协议不同)

请注意:localhost127.0.0.1虽然都指向本机,但也属于跨域。

在一个http请求中,http头部RefererOrigin字段标识了当前域名,Host字段标识了此时请求的域名。

故,如果我们在当前的js页面,通过ajax请求第三方的数据,就会出现浏览器的跨域问题。

解决跨域问题

解决跨域问题,有如下三种方式:

1、使用jsonp

2、服务器代理

3、在服务端设置response headerAccess-Control-Allow-Origin字段。

使用jsonp

jsonp解决跨域问题的原理是,浏览器的script标签是不受同源策略限制的,我们可以在script标签中访问任何域名下的资源文件。利用这一特性,用script标签从服务器中请求数据,同时服务器返回一个带有方法和数据的js代码,请求完成,调用本地的js方法,来完成数据的处理。

前端实现,以Jqueryajax方法为例:

[javascript]  view plain  copy
  1. $.ajax({    
  2.         url:"",    
  3.         dataType:'jsonp',    
  4.         data:'',    
  5.         jsonp:'callback',  //传递给请求处理程序或页面的,用以获得jsonp回调函数名的参数名(默认为:callback)  
  6.   
  7.         success:function(result) {    
  8.             //成功的处理  
  9.         },  
  10.         error:function(){  
  11.             //错误处理  
  12. }   
  13. });    

服务端此时返回的不能是普通的json字符串,而是一段可以被前端js执行的一段js代码。

比较一下jsonjsonp格式的区别:

json格式:

[plain]  view plain  copy
  1. {  
  2.     "message":"获取成功",  
  3.     "state":"1",  
  4.     "result":{"name":"工作组1","id":1,"description":"11"}  
  5. }  

jsonp格式:

[plain]  view plain  copy
  1. callback({  
  2.     "message":"获取成功",  
  3.     "state":"1",  
  4.     "result":{"name":"工作组1","id":1,"description":"11"}  
  5. })  

从格式来看,jsonp是在json的基础上包装了一个方法名,此方法名是前端请求传过来的,如请求地址为:http://localhost:9999/tookApp/tbk/getItem?callback=JSONP_CALLBACK,那么方法名就是JSONP_CALLBACK

下面提供一段java代码,对象转jsonp的工具类:

[java]  view plain  copy
  1. package com.tooklili.app.web.util;  
  2.   
  3. import javax.servlet.http.HttpServletRequest;  
  4.   
  5. import org.apache.commons.lang.StringUtils;  
  6. import org.springframework.web.context.request.RequestContextHolder;  
  7. import org.springframework.web.context.request.ServletRequestAttributes;  
  8.   
  9. import com.fasterxml.jackson.databind.util.JSONPObject;  
  10.   
  11. /** 
  12.  *  
  13.  * @author ding.shuai 
  14.  * @date 2016年8月15日上午9:47:02 
  15.  */  
  16. public class AppUtil {  
  17.       
  18.     /** 
  19.      * 判断json字符串是否需要转化成jsonp格式 
  20.      * @param request 
  21.      * @param result 
  22.      * @return 
  23.      */  
  24.     public static Object conversionJsonp(Object result){  
  25.         HttpServletRequest request = ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest();  
  26.         return conversionJsonp(request, result);  
  27.     }  
  28.       
  29.   
  30.     public static Object conversionJsonp(HttpServletRequest request,Object result){  
  31.         String callback = request.getParameter("callback");  
  32.         if(StringUtils.isNotEmpty(callback)){  
  33.             return new JSONPObject(callback, result);  
  34.         }  
  35.         return result;  
  36.     }  
  37. }  

jsonp的缺点:

1、JSONP是一种非官方的方法,而且这种方法只支持GET方法,不如POST方法安全。(从实现机制就可明白)。

2、JSONP的实现需要服务器配合,如果是访问的是第三方的服务器,我们没有修改服务器的权限,那么这种方式是不可行的。

服务器代理

这种方式运用的就是服务器的反向代理技术,控制客户端和服务器的访问都从代理服务器经过,比如用nginx作为服务器代理,在nginx上配置客户端和第三方服务的反向代理,这样就可保证客户端、第三方是同源的了,同一个源,都来自代理服务器。

关于nginx的反向代理配置,可访问我的这篇博客:http://blog.csdn.net/csdn_ds/article/details/58605591

服务器代理的缺点:

开发比较麻烦,对开发环境比较严格,需要在本机上配置代理服务器。

优点:

完美解决使用jsonp,第三方服务没有修改权限的问题。程序的代码侵入性小,代码级别不需要考虑跨域问题。

在服务端设置response headerAccess-Control-Allow-Origin字段

在被请求的Response Header中加入如下代码:

[java]  view plain  copy
  1. // 指定允许其他域名访问   
  2. response.setHeader("Access-Control-Allow-Origin""*");  
  3. // 响应类型   
  4. response.setHeader("Access-Control-Allow-Methods""POST");  
  5. // 响应头设置   
  6. response.setHeader("Access-Control-Allow-Headers""x-requested-with,content-type");  

如果所有请求都想让其他域名的服务通过浏览器ajax请求到,可以通过Filter统一设置response header

[java]  view plain  copy
  1. package com.tooklili.app.web.filter;  
  2.   
  3. import java.io.IOException;  
  4.   
  5. import javax.servlet.Filter;  
  6. import javax.servlet.FilterChain;  
  7. import javax.servlet.FilterConfig;  
  8. import javax.servlet.ServletException;  
  9. import javax.servlet.ServletRequest;  
  10. import javax.servlet.ServletResponse;  
  11. import javax.servlet.http.HttpServletResponse;  
  12.   
  13. /** 
  14.  * 设置公共属性的过滤器 
  15.  * @author shuai.ding 
  16.  * 
  17.  * @date 2017年6月21日上午11:02:27 
  18.  */  
  19. public class CommonSetFilter implements Filter{  
  20.   
  21.     @Override  
  22.     public void init(FilterConfig filterConfig) throws ServletException {         
  23.     }  
  24.   
  25.     @Override  
  26.     public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)  
  27.             throws IOException, ServletException {  
  28.         //解决跨域问题  
  29.         HttpServletResponse httpServletResponse =(HttpServletResponse)response;  
  30.         // 指定允许其他域名访问   
  31.         httpServletResponse.setHeader("Access-Control-Allow-Origin""*");  
  32.         // 响应类型   
  33.         httpServletResponse.setHeader("Access-Control-Allow-Methods""POST");  
  34.         // 响应头设置   
  35.         httpServletResponse.setHeader("Access-Control-Allow-Headers""x-requested-with,content-type");  
  36.           
  37.         chain.doFilter(request, response);  
  38.     }  
  39.   
  40.     @Override  
  41.     public void destroy() {   
  42.     }  
  43.   
  44. }  

此处说明一下,笔者亲测:只设置Access-Control-Allow-Origin属性也是可以的。

Access-Control-Allow-Origin:* 表示允许任何域名跨域访问

如果需要指定某域名才允许跨域访问,只需把Access-Control-Allow-Origin:*改为Access-Control-Allow-Origin:允许的域名

例如:response.setHeader(“Access-Control-Allow-Origin”,”http://www.client.com”);

缺点:

1、此种解决跨域方案,需要浏览器支持H5,因为这是HTML5解决跨域的方式,如果产品面向的是PC端,这种方式可能就不是一个好的解决方案,如果面向的是手机端,此方法不为一个简单、粗暴的好方式。

2、设置*,存在安全隐患。

总结

综上三种解决跨域的方案,个人感觉使用服务代理最好,没有破坏浏览器的安全策略,但这个对开发环境要高一点。设置response header的方式,根据具体情况分析,要考虑清楚产品面向的用户。对于jsonp这种方式,虽然没有破坏浏览器的安全策略,但只支持get方式的请求,有点不能接受,因为get传输有参数长度的限制,同时又要考虑传输中文的乱码问题,但如果项目中只是简单的查询、展示,这种方式还是可以考虑的。

其他好文推荐:

Access-Control-Allow-Origin 跨域设置多域名http://www.jianshu.com/p/b587dd1b7086

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值