1.理论
当两个域具有相同的协议(如http), 相同的端口(如80),相同的host(如www.example.org),那么我们就可以认为它们是相同的域。比如http://www.example.org/index.html
和http://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都有读写的权限)。
2.2 window.postMessage
window.postMessage是HTML5定义的一个很新的方法,这个方法可以很方便地跨window通信。由于它是一个很新的方法,所以在比较旧的浏览器中都无法使用,如:FireFox4.0、IE6。
2.3 CORS
CORS背后的基本思想就是使用自定义的HTTP头部让浏览器与服务器进行沟通,从而决定请求或响应是应该成功还是失败。服务端会在HTTP请求头中增加一系列HTTP请求参数(例如Access-Control-Allow-Origin等),来限制哪些域的请求和哪些请求类型可以接受,而客户端在发起请求时必须声明自己的源(Orgin),否则服务器将不予处理,如果客户端不作声明,请求甚至会被浏览器直接拦截都到不了服务端。服务端收到HTTP请求后会进行域的比较,只有同域的请求才会处理。注意的是旧版本的浏览器也是不支持的
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();
}
}
}