09.19
在使用ajax的时候,浏览器会对调用的地址做限制。只有在相同协议、相同域名、相同端口的情况下才能调用成功。
例如,一个网页是http://www.abc.com/abc.html,在这个页面上,下面的ajax调用将不会成功:
(1)https://www.abc.com/*(*表示任意的字母或斜线)(由于使用了不同的协议)
(2)http://www.abcd.com/* 或 http://www1.abc.com/*(由于不是相同的域名。一级域名相同,二级或以下域名不相同,也会被认为是不同的域名)
(3)http://www.abc.com:8080/* (由于端口不同)
可以使用以下几种方法来解决ajax跨域问题。
我们使用a.test.com和b.test.com 2个域名。b.test.com下的html页面使用ajax方式调用a.test.com下的接口。
打开eclipse,新建1个dynamic web project,工程名为crossSite,当作a.test.com。新建1个tomcat server。
再建一个static web project,工程名为crossSite2,当作b.test.com。
使用nginx给2个域名做反向代理。在nginx里做如下配置:
server { listen 80; server_name a.test.com; location / { proxy_pass http://localhost:8080/crossSite/; } } server { listen 80; server_name b.test.com; location / { root crossSite2的存储目录\\WebContent\\; index index.html; } }
修改本机的host文件,2个域名都指向127.0.0.1。
(1)jsonp
jsonp的原理基于以下:html页面里的script标签可以引用的地址不受限制。比如:http://b.test.com/index.html这个页面里可以包含script标签,它的src为”http://a.test.com/CrossSiteServlet”。
在crossSite工程里创建一个servlet,代码如下:
import java.io.IOException; import java.util.HashMap; import java.util.Map; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.json.JSONObject; /** * Servlet implementation class CrossSiteServlet */ @WebServlet("/CrossSiteServlet") public class CrossSiteServlet extends HttpServlet { private static final long serialVersionUID = 1L; public CrossSiteServlet() { super(); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setCharacterEncoding("utf-8"); String callback = request.getParameter("callback"); Map<String, String> map = new HashMap<String, String>(); map.put("id", "id1"); map.put("name", "name1"); JSONObject jsonObj = new JSONObject(map); String returnStr = callback + "(" + jsonObj.toString() + ")"; response.getWriter().println(returnStr); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
当我们访问http://a.test.com/CrossSiteServlet?callback=callback,将返回如下的数据:callback({“id”:”id1″,”name”:”name1″}),即一个js函数名以及json格式的数据。
在crossSite2工程webContent目录下新建一个html页面:index.html。内容如下:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Insert title here</title> <script src="http://code.jquery.com/jquery-1.8.0.min.js"></script> <script type="text/javascript"> $().ready(function () { $.ajax({ type: "get", async: true, url: "http://a.test.com/CrossSiteServlet", data:{}, dataType: "jsonp", jsonp: "callback", //传递给请求处理程序或页面的,用以获得jsonp回调函数名的参数名(一般默认为:callback) success: function (json) { alert(json.id+" "+json.name); }, error: function () { alert('fail'); } }); }); </script> </head> <body> jsonp test </body> </html>
注意上面的jsonp后面的值为callback,这个值必须同上面servlet返回的js函数名一致。
(2)iframe
原理:在页面里嵌套一个iframe,这个iframe的src属性指向不同的域名,从而可以从这个iframe里获取到其它域名下的数据。
iframe也有跨域限制,正常情况下,b.test.com域名下的网页是不能访问它内部指向a.test.com域名的iframe里的内容的。
可以通过修改页面和iframe的document.domain属性,让它们都为一级域名test.com,这样就可以跨域访问了。
首先在crossSite2工程WebContent目录下新建index2.html,内容如下:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Insert title here</title> <script src="http://code.jquery.com/jquery-1.8.0.min.js"></script> <script type="text/javascript"> document.domain='test.com'; function getData(){ document.getElementById('ifrm1').src="http://a.test.com/IframeCrossSiteServlet"; //setTimeout(function(){alert($("#ifrm1").contents().find("div1").text())}, 3000); setTimeout(function(){alert($(document.getElementById('ifrm1').contentWindow.document.body).find('#div1').text())}, 3000); } </script> </head> <body> <iframe id="ifrm1" name="ifrm1" style="display: none;"></iframe> <button type="button" onclick="getData()">get data</button> </body> </html>
注意上面将document.domain修改为‘test.com’,如果去掉这句就会报错。
然后在crossSite工程里新建一个servlet:IframeCrossSiteServlet。代码如下:
import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * Servlet implementation class IframeCrossSiteServlet */ @WebServlet("/IframeCrossSiteServlet") public class IframeCrossSiteServlet extends HttpServlet { private static final long serialVersionUID = 1L; public IframeCrossSiteServlet() { super(); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.getWriter().append("<script type=\"text/javascript\">\r\n" + "document.domain='test.com';\r\n</script> <DIV id=\"div1\">I am an iframe.</DIV> "); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
注意上面在doGet方法里,同样地将document.domain的值改为‘test.com’。
以上2种方法存在的问题:
1. jsonp只能使用get调用。即使在代码里改成post调用,实际上还是使用get调用。所以不够安全。
2. iframe只能在一级域名相同的情况下使用。如果一级域名不同,就不能使用了。