跨域常用解决方案

Reading time ~33 minutes

1.理论

当两个域具有相同的协议(如http), 相同的端口(如80),相同的host(如www.example.org),那么我们就可以认为它们是相同的域。比如http://www.example.org/index.htmlhttp://www.example.org/sub/index.html 是同域,而 http://www.example.org , https://www.example.org , http://www.example.org:8080 , http://sub.example.org 中的任何两个都将构成跨域。

同源策略的限制之一就是不能通过ajax的方法去请求不同源中的资源,不管是静态页面、动态网页还是web服务。第二个限制是浏览器中不同域的框架之间是不能进行js的交互操作的,不同的框架之间是可以获取window对象的,但却无法获取相应的属性和方法。

2.常用的几种跨域解决方案

2.1 window.name

window 对象的name属性是一个很特别的属性,当该window的location变化,然后重新加载,它的name属性可以依然保持不变(窗口载入的所有的页面都是共享一个window.name的,每个页面对window.name都有读写的权限)。

window.name实现的跨域数据传输的实现

2.2 window.postMessage

window.postMessage是HTML5定义的一个很新的方法,这个方法可以很方便地跨window通信。由于它是一个很新的方法,所以在比较旧的浏览器中都无法使用,如:FireFox4.0、IE6。

window.postMessage实现跨域通信实现

2.3 CORS

CORS背后的基本思想就是使用自定义的HTTP头部让浏览器与服务器进行沟通,从而决定请求或响应是应该成功还是失败。服务端会在HTTP请求头中增加一系列HTTP请求参数(例如Access-Control-Allow-Origin等),来限制哪些域的请求和哪些请求类型可以接受,而客户端在发起请求时必须声明自己的源(Orgin),否则服务器将不予处理,如果客户端不作声明,请求甚至会被浏览器直接拦截都到不了服务端。服务端收到HTTP请求后会进行域的比较,只有同域的请求才会处理。注意的是旧版本的浏览器也是不支持的

Java之旅–跨域(CORS)

2.4 document.domain

document.domain的方法只适用于不同子域的框架间的交互,当我们把它们document的domain属性都修改为a.com,浏览器就会认为它们处于同一个域下,那么我们就可以互相调用对方的method来通信了。但要注意的是,document.domain的设置是有限制的,我们只能把document.domain设置成自身或更高一级的父域,且主域必须相同

1.在页面 http://www.example.com/a.html 中设置document.domain:

<iframe id = "iframe" src="http://example.com/b.html" onload = "test()"></iframe>
<script type="text/javascript">
    document.domain = 'example.com';//设置成主域
    function test(){
        alert(document.getElementById('iframe').contentWindow);//contentWindow 可取得子窗口的 window 对象
    }
</script>

2.在页面 http://example.com/b.html 中也设置document.domain:

<script type="text/javascript">
    document.domain = 'example.com';//在iframe载入这个页面也设置document.domain,使之与主页面的document.domain相同
</script>

2.5 JONP

在同源策略下,在某个服务器下的页面是无法获取到该服务器以外的数据的,但img、iframe、script等标签是个例外,这些标签可以通过src属性请求到其他服务器上的数据。而JSONP就是通过script节点src属性调用跨域的请求。

JSONP的优点:易于实现,是在受信任的双方传递数据,JSONP是非常合适的选择。

JSONP的缺点:存在一些安全隐患,如果第三方的脚本随意地执行,那么它就可以篡改页面内容,截获敏感数据;另外jsonp支持GET请求而不支持POST等其它类型的HTTP请求;它只支持跨域HTTP请求这种情况,不能解决不同域的两个页面之间如何进行JavaScript调用的问题。

//-------------------------javascript代码:
var url = 跨域的dns + "/fzyw/xzfy/smcl/autoUpdateByWS.action";
var data = {
  "writid": writid,
  "reportName": reportname
};

$.ajax({
    contentType: "application/x-www-form-urlencoded;charset=UTF-8",
    type : "GET",
    url : url,
    data : data,
    cache : false, //默认值true
    dataType : "jsonp",
    jsonp: "callback", // 必须,返回的响应需要以此为前缀
    //使用了jsonp 方式,则此方法不被触发.原因可能是dataType如果指定为jsonp的话,就已经不是ajax事件了
    beforeSend: function(){

    },
    success : function(json){
        if (json.result != "success") {
           alert("自动更新扫描材料失败!");
        }
    },
    complete: function(XMLHttpRequest, textStatus){
        $.unblockUI({ fadeOut: 10 });
    },
    //使用了jsonp方式,则此方法不被触发.原因可能是dataType如果指定为jsonp的话,就已经不是ajax事件了
    error: function(xhr){ //请求出错处理
        alert("请求出错(请检查相关度网络状况.)");
    }
});
//---------------------------java后台代码:
@RequestMapping("/autoUpdateByWS")
public @ResponseBody
String autoUpdateByWS(Model model, String callback, String writid,
     String reportName) {
  /**
  *业务代码
  */
  .....

  String response = "${callback}({\"result\":\"success\"})"
       .replaceFirst("\\$\\{callback\\}", callback);
  return response;
}

2.6 webservice跨域问题

服务端直接的访问是不受跨域影响的,所以由js调用后端,后端通过ws方式访问远程服务

//----------------js代码:
$(function(){
$("#btn2").click(function(){
                var name = document.getElementById("name").value;
                $.post(
                      "HttpURLConnectionServlet",
                      "name="+name,
                      function(msg) {
                           //alert(msg);
                           var $Result = $(msg);
                           var value = $Result.find("return").text();
                           alert(value);
                      },
                      "xml"
                );
           });
}
//-----------------java代码:
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
 * 使用HttpURLConnection发送webservice请求
 */
public class HttpURLConnectionServlet extends HttpServlet {
      private static final long serialVersionUID = 1L;
      protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
           String name = request.getParameter("name");
           String data = "<soap:Envelope xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/'>
<soap:Body><ns2:sayHello xmlns:ns2='http://ws.day01_ws.atguigu.com/'><arg0>"+name+"</arg0>
</ns2:sayHello></soap:Body></soap:Envelope>";//需要发送的soap消息
           URL url = new URL("http://192.168.10.165:8888/day01_ws/datatypews");
           HttpURLConnection connection = (HttpURLConnection) url.openConnection();
           connection.setRequestMethod("POST");//请求方式
           connection.setDoOutput(true);//是否要输出数据到服务器端
           connection.setDoInput(true); //是否接受数据的输入
           connection.setRequestProperty("Content-Type", "text/xml;charset=utf-8");//请求的参数
           OutputStream os = connection.getOutputStream();
           os.write(data.getBytes("utf-8"));
           int responseCode = connection.getResponseCode();
           if(responseCode==200) {//成功返回时,
                InputStream is = connection.getInputStream();//String xml
                System.out.println("return "+is.available());
                response.setContentType("text/xml;charset=utf-8");
                ServletOutputStream outputStream = response.getOutputStream();
                byte[] buffer = new byte[1024];
                int len = 0;
                while((len=is.read(buffer))>0) {
                      outputStream.write(buffer, 0, len);
                }
                outputStream.flush();
           }
      }
}

3.参考资料

  1. window.name实现的跨域数据传输的实现

  2. window.postMessage实现跨域通信实现

  3. Java之旅–跨域(CORS)

jdk-timer、spring-task、quartz的比较

这篇文章将简单介绍目前常用的定时任务框架! Continue reading

小岛经济学-读书笔记

Published on May 10, 2018

java常见的http请求库

Published on May 05, 2018