跨域问题解决方案(HttpClient安全跨域 & jsonp跨域)

来源:互联网 发布:mac win启动U盘 编辑:程序博客网 时间:2024/05/13 00:18

http://blog.csdn.net/lovesummerforever/article/details/38052213

1 错误场景                                         

       今天要把项目部署到外网的时候,出现了这样的问题, 我把两个项目放到自己本机的tomcat下, 进行代码调试, 运行

都没有问题的, 一旦把我需要调用接口的项目B放到其他的服务器上, 就会报错, 无法通过Ajax调用springMVC的接口,

这是什么原因呢?

      当我使用json ajax post请求传递数据的时候在web端出错:XMLHttpRequest cannot loadhttp://ip:8082/security/auth/outside.do. Origin http://ip:8080 is not allowed by Access-Control-Allow-Origin.

 

2 初识jsonp                               

      经过在网上查找, 网上大多数说是跨域问题. 解决跨域问题据说是jsonp, 百度了一篇文章, 不管三七二十一就一下

子把ajax传递的数据类型dataType 改成为jsonp, 并使用get方式, 单纯的认为, json和jsonp没啥区别, 运行, 报错, 如

下图所示:

 

      没有了上述的 is not allowed ....的错误, 变成了只剩下500的错误, 说明jsonp起了些作用, 我的bug的问题就是在于网上说的"跨域" 。而究竟什么是跨域呢?

 

3 什么是跨域?什么是不跨域?           

      上没有过多的去测试,一句话:同一个ip、同一个网络协议、同一个端口,三者都满足就是同一个域,否则就是

跨域问题了。而为什么开发者最初不直接定为一切可跨域的呢?默认的为什么都是不可跨域呢?这就涉及到了同源策

略,为了系统的安全,由Netscape提出一个著名的安全策略。现在所有支持JavaScript的浏览器都会使用这个策略。

所谓同源是,域名,协议,端口相同。当我们在浏览器中打开百度和谷歌两个网站时,百度浏览器在执行一个脚本的

时候会检查这个脚本属于哪个页面的,即检查是否同源,只有和百度同源的脚本才会被执行,如果没有同源策略,那

随便的向百度中注入一个js脚本,弹个恶意广告,通过js窃取信息,这就很不安全了。

 

4 跨域问题如何解决?jsonp为什么能解决跨域问题?和json有什么区别?

      关于解决跨域问题,有多种解决方案,解决方案如下。

 

      4.1 方案一

      ajax请求地址改为自己系统的后台地址,之后在自己的后台用HttpClient请求url。封装好的跨域请求url工具类

代码如下所示。

 

  1. <span style="font-size:18px;">@SuppressWarnings("all")  
  2. public final class UrlUtil {  
  3.   
  4.     private static HttpClient httpClient = new HttpClient();  
  5.   
  6.     /** 
  7.      * @Title: getDataFromURL 
  8.      * @Description: 根据URL跨域获取输出结果,支持http 
  9.      * @param strURL 
  10.      *            要访问的URL地址 
  11.      * @param param 
  12.      *            参数 
  13.      * @return 结果字符串 
  14.      * @throws Exception 
  15.      */  
  16.     public static String getDataFromURL(String strURL, Map<String, String> param) throws Exception {  
  17.         URL url = new URL(strURL);  
  18.         URLConnection conn = url.openConnection();  
  19.         conn.setDoOutput(true);  
  20.   
  21.         OutputStreamWriter writer = new OutputStreamWriter(conn.getOutputStream());  
  22.         final StringBuilder sb = new StringBuilder(param.size() << 4); // 4次方  
  23.         final Set<String> keys = param.keySet();  
  24.         for (final String key : keys) {  
  25.             final String value = param.get(key);  
  26.             sb.append(key); // 不能包含特殊字符  
  27.             sb.append('=');  
  28.             sb.append(value);  
  29.             sb.append('&');  
  30.         }  
  31.         // 将最后的 '&' 去掉  
  32.         sb.deleteCharAt(sb.length() - 1);  
  33.         writer.write(sb.toString());  
  34.         writer.flush();  
  35.         writer.close();  
  36.   
  37.         InputStreamReader reder = new InputStreamReader(conn.getInputStream(), "utf-8");  
  38.         BufferedReader breader = new BufferedReader(reder);  
  39.         // BufferedWriter w = new BufferedWriter(new FileWriter("d:/1.txt"));  
  40.         String content = null;  
  41.         String result = null;  
  42.         while ((content = breader.readLine()) != null) {  
  43.             result += content;  
  44.         }  
  45.   
  46.         return result;  
  47.     }  
  48.   
  49.     /** 
  50.      * @Title: postMethod 
  51.      * @Description: 根据URL跨域获取输出结果,支持https 
  52.      * @param url 
  53.      *            要访问的URL地址(http://www.xxx.com?) 
  54.      * @param urlParm 
  55.      *            参数(id=1212&pwd=2332) 
  56.      * @return 结果字符串 
  57.      */  
  58.     public static String postMethod(String url, String urlParm) {  
  59.         if (null == url || "".equals(url)) {  
  60.             // url = "http://www.baidu.com";  
  61.             return null;  
  62.         }  
  63.         PostMethod post = new PostMethod(url); // new UTF8PostMethod(url);  
  64.         if (null != urlParm && !"".equals(urlParm)) {  
  65.             String[] arr = urlParm.split("&");  
  66.             NameValuePair[] data = new NameValuePair[arr.length];  
  67.             for (int i = 0; i < arr.length; i++) {  
  68.                 String name = arr[i].substring(0, arr[i].lastIndexOf("="));  
  69.                 String value = arr[i].substring(arr[i].lastIndexOf("=") + 1);  
  70.                 data[i] = new NameValuePair(name, value);  
  71.             }  
  72.             post.setRequestBody(data);  
  73.         }  
  74.         int statusCode = 0;  
  75.         String pageContent = "";  
  76.         try {  
  77.             statusCode = httpClient.executeMethod(post);  
  78.             if (statusCode == HttpStatus.SC_OK || statusCode == HttpStatus.SC_MOVED_TEMPORARILY) {  
  79.                 pageContent = post.getResponseBodyAsString();  
  80.                 return pageContent;  
  81.             }  
  82.         } catch (Exception e) {  
  83.             e.printStackTrace();  
  84.             return null;  
  85.         } finally {  
  86.             post.releaseConnection();  
  87.         }  
  88.         return null;  
  89.     }  
  90.   
  91.     public static String doPost(String url, String json) throws Exception {  
  92.         PostMethod postMethod = new PostMethod(url);  
  93.         StringRequestEntity requestEntity = new StringRequestEntity(json, "application/json""UTF-8");  
  94.         postMethod.setRequestEntity(requestEntity);  
  95.         /* 发送请求,并获取响应对象 */  
  96.         int statusCode = httpClient.executeMethod(postMethod);  
  97.         String result = null;  
  98.         if (statusCode == HttpStatus.SC_OK) {  
  99.             result = postMethod.getResponseBodyAsString();  
  100.         } else {  
  101.             System.out.println("Method failed: " + postMethod.getStatusLine());  
  102.         }  
  103.         return result;  
  104.     }  
  105.   
  106.     public static String post(String url, Map<String, String> params) {  
  107.         DefaultHttpClient httpclient = new DefaultHttpClient();  
  108.         String body = null;  
  109.         HttpPost post = postForm(url, params);  
  110.         body = invoke(httpclient, post);  
  111.         httpclient.getConnectionManager().shutdown();  
  112.         return body;  
  113.     }  
  114.   
  115.     private static HttpPost postForm(String url, Map<String, String> params) {  
  116.         HttpPost httpost = new HttpPost(url);  
  117.         List<BasicNameValuePair> nvps = new ArrayList<BasicNameValuePair>();  
  118.         Set<String> keySet = params.keySet();  
  119.         for (String key : keySet) {  
  120.             BasicNameValuePair basicNameValuePair = new BasicNameValuePair(key, params.get(key));  
  121.             nvps.add(basicNameValuePair);  
  122.         }  
  123.         try {  
  124.             httpost.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8));  
  125.         } catch (UnsupportedEncodingException e) {  
  126.             e.printStackTrace();  
  127.         }  
  128.         return httpost;  
  129.     }  
  130.   
  131.     private static String invoke(DefaultHttpClient httpclient, HttpUriRequest httpost) {  
  132.         HttpResponse response = sendRequest(httpclient, httpost);  
  133.         String body = paseResponse(response);  
  134.         return body;  
  135.     }  
  136.   
  137.     private static HttpResponse sendRequest(DefaultHttpClient httpclient, HttpUriRequest httpost) {  
  138.         HttpResponse response = null;  
  139.         try {  
  140.             response = httpclient.execute(httpost);  
  141.         } catch (Exception e) {  
  142.             e.printStackTrace();  
  143.         }  
  144.         return response;  
  145.     }  
  146.   
  147.     private static String paseResponse(HttpResponse response) {  
  148.         HttpEntity entity = response.getEntity();  
  149.         String body = null;  
  150.         try {  
  151.             body = EntityUtils.toString(entity);  
  152.         } catch (Exception e) {  
  153.             e.printStackTrace();  
  154.         }  
  155.         return body;  
  156.     }  
  157.   
  158.     public static void main(String[] args) throws Exception {  
  159.         String url = "http://ip:8082/security/auth/outside.do";  
  160.         Map<String, String> map = new HashMap<String, String>();  
  161.         map.put("loginName""root");  
  162.         map.put("code""vms2.0");  
  163.         String msg = post(url, map);  
  164.         JSONArray jary = JsonUtil.Json2JSONArray(msg);  
  165.         for (int i = 0; i < jary.length(); i++) {  
  166.             JSONObject obj = jary.getJSONObject(i);  
  167.             System.out.println(obj);  
  168. //          System.out.print(obj.getString("classid"));  
  169. //          System.out.print("\t"+obj.getString("classname"));  
  170. //          System.out.println("\t"+obj.getString("sonclass"));  
  171.         }  
  172. //      System.out.println(jary);  
  173.     }  
  174. }</span>  


       当然要导入httpclient-4.3.1.jar包到自己的项目中哦。这样把请求的参数内容放到map中,通过HttpClent来实现跨域请求。

       4.2 解决方案二

       两个系统之间的数据传递是通过ajax post请求,传递json的方式来完成,我们在这里可以使用jsonp的方式,但

是json和jsonp截然不同。首先说说神马是json,再说神马是jsonp。

json

       全拼(javaScript Object Notation)轻量级的数据交换格式,易于机器解析和生成。基于javaScript

Programming Language,StandardECMA Edition December1999的一个子集。json完全独立于语言的文本格

式,但是也使用类似于C语言家族的习惯(include c c++ c# java javaScript perl python)等,这些特性使得json

成为理想的数据交换语言。格式为key,value格式,具体就不赘述了。

jsonp

       jsonp全拼是(json with Padding)是json的一种使用模式,padding意思为填料,垫料,填充,填补。json可

以说是名词,而jsonp是动宾短语,两者有联系,但是有本质的区别,就像米饭和把米饭填充到碗里一样,那米饭和

米饭填是一样的么,我们自然明了了。

 

       jsonp算是钻空子实现的跨域,到底通过jsonp具体如何解决跨域问题呢?本篇过长了,我们下篇分晓。goodnight...

 

 

本篇文章主要是对使用jQuery与JSONP轻松解决跨域访问的问题进行了详细的介绍,需要的朋友可以过来参考下,希望对大家有所帮助

时间过得好快,又被拉回js战场时, 跨域问题这个伤疤又开疼了.

好在,有jquery帮忙,跨域问题似乎没那么难缠了.这次也借此机会对跨域问题来给刨根问底,结合实际的开发项目,查阅了相关资料,算是解决了跨域问题..有必要记下来备忘.

跨域的安全限制都是指浏览器端来说的.服务器端是不存在跨域安全限制的,所以通过本机服务器端通过类似httpclient方式完成“跨域访问”的工作,然后在浏览器端用AJAX获取本机服务器端“跨域访问”对应的url.来间接完成跨域访问也是可以的.但很显然开发量比较大,但限制也最少,很多widget开放平台server端(如sohu博客开放平台)其实就么搞的.不在本次讨论范围.

要讨论的是浏览器端的真正跨域访问,推荐的是目前jQuery $.ajax()支持get方式的跨域,这其实是采用jsonp的方式来完成的.

真实案例:

复制代码代码如下:

var qsData = {'searchWord':$("#searchWord").attr("value"),'currentUserId':$("#currentUserId").attr("value"),'conditionBean.pageSize':$("#pageSize").attr("value")};

$.ajax({
   async:false,
   url: http://跨域的dns/document!searchJSONResult.action,
   type: "GET",
   dataType: 'jsonp',
   jsonp: 'jsoncallback',
   data: qsData,
   timeout: 5000,
   beforeSend: function(){
   //jsonp 方式此方法不被触发.原因可能是dataType如果指定为jsonp的话,就已经不是ajax事件了
   },
   success: function (json) {//客户端jquery预先定义好的callback函数,成功获取跨域服务器上的json数据后,会动态执行这个callback函数
    if(json.actionErrors.length!=0){
           alert(json.actionErrors);
     }
       genDynamicContent(qsData,type,json);
   },
    complete: function(XMLHttpRequest, textStatus){
    $.unblockUI({ fadeOut: 10 }); 
   },
   error: function(xhr){
    //jsonp 方式此方法不被触发.原因可能是dataType如果指定为jsonp的话,就已经不是ajax事件了
    //请求出错处理
    alert("请求出错(请检查相关度网络状况.)");
   }
});


注意:
复制代码代码如下:

$.getJSON(" http://跨域的dns/document!searchJSONResult.action?name1="+value1+"&jsoncallback=?",
      function(json){
      if(json.属性名==值){
      // 执行代码
            }
        });

这种方式其实是上例$.ajax({..}) api的一种高级封装,有些$.ajax api底层的参数就被封装而不可见了.
这样,jquery就会拼装成如下的url get请求
http://跨域的dns/document!searchJSONResult.action?&jsoncallback=jsonp1236827957501&_=1236828192549&searchWord=%E7%94%A8%E4%BE%8B¤tUserId=5351&conditionBean.pageSize=15

在响应端(http://跨域的dns/document!searchJSONResult.action),
通过 jsoncallback = request.getParameter("jsoncallback") 得到jquery端随后要回调的js function name:jsonp1236827957501
然后 response的内容为一个Script Tags:"jsonp1236827957501("+按请求参数生成的json数组+")"; 
jquery就会通过回调方法动态加载调用这个js tag:jsonp1236827957501(json数组); 
这样就达到了跨域数据交换的目的.

jsonp的最基本的原理是:动态添加一个<script>标签,而script标签的src属性是没有跨域的限制的。这样说来,这种跨域方式其实与ajax XmlHttpRequest协议无关了.
这样其实"jQuery AJAX跨域问题"就成了个伪命题了,jquery $.ajax方法名有误导人之嫌.

如果设为dataType: 'jsonp', 这个$.ajax方法就和ajax XmlHttpRequest没什么关系了,取而代之的则是JSONP协议.

JSONP是一个非官方的协议,它允许在服务器端集成Script tags返回至客户端,通过javascript callback的形式实现跨域访问JSONP即JSON with Padding。由于同源策略的限制,XmlHttpRequest只允许请求当前源(域名、协议、端口)的资源。如果要进行跨域请求,我们可以通过使用html的script标记来进行跨域请求,并在响应中返回要执行的script代码,其中可以直接使用JSON传递javascript对象。这种跨域的通讯方式称为JSONP。

jsonCallback 函数jsonp1236827957501(....): 是浏览器客户端注册的,获取跨域服务器上的json数据后,回调的函数

Jsonp原理:

首先在客户端注册一个callback (如:'jsoncallback'), 然后把callback的名字(如:jsonp1236827957501)传给服务器。注意:服务端得到callback的数值后,要用jsonp1236827957501(......)把将要输出的json内容包括起来,此时,服务器生成 json 数据才能被客户端正确接收。

然后以 javascript 语法的方式,生成一个function , function 名字就是传递上来的参数 'jsoncallback'的值 jsonp1236827957501 .

最后将 json 数据直接以入参的方式,放置到 function 中,这样就生成了一段 js 语法的文档,返回给客户端。

客户端浏览器,解析script标签,并执行返回的 javascript 文档,此时javascript文档数据,作为参数,
传入到了客户端预先定义好的 callback 函数(如上例中jquery $.ajax()方法封装的的success: function (json))里.(动态执行回调函数)

可以说jsonp的方式原理上和<script src="http://跨域/...xx.js"></script>是一致的(qq空间就是大量采用这种方式来实现跨域数据交换的) .JSONP是一种脚本注入(Script Injection)行为,所以也有一定的安全隐患.

原理的示例代码:

复制代码代码如下:

//客户端的JAVASCRIPT代码 
var script=document.createElement("script"); 
script.src="http://www.pl4cj.com:8888/5/6/action.php?param=123&callback="+fnName; 
document.getElementsByTagName("head")[0].appendChild(script) 

//服务器端的PHP代码,一定要有callback来进行回调,在这里加上括号,是让它以语句块的方式来进行解析 
<?php 
<SPAN style="COLOR: #ff00ff">echo $_GET["callback"]."(".json_encode($_GET).");"; 
</SPAN>?

注意,jquey是不支持post方式跨域的.

为什么呢?
虽然采用post +动态生成iframe是可以达到post跨域的目的,但这样做是一个比较极端的方式,不建议采用.
也可以说get方式的跨域是合法的,post方式从安全角度上,被认为是不合法的, 万不得已还是不要剑走偏锋..

client端跨域访问的需求看来也引起w3c的注意了,看资料说html5 WebSocket标准支持跨域的数据交换,应该也是一个将来可选的跨域数据交换的解决方案

0 0
原创粉丝点击