前言:在实际的开发应用过程中,很有可能需要获取另外一个服务器上的资源,如果两个服务器的域名、端口、协议等不同,就存在跨域问题。
一、什么是跨域?
简单的来讲就是浏览器不能执行其他网站的脚本,主要原因在于浏览器的同源策略。而同源策略又分成:
1.DOM同源策略
2.XMLHttpRequest同源策略
这里主要讲的是如何解决XMLHttpRequest同源策略。
比如有两个服务器,分别是A服务器(www.servicea.com)和B服务器(www.serviceb.com),A服务器向B服务器发起一个ajax请求,这时候就会产生跨域问题。
二、哪些情况存在跨域问题?
当端口、协议、域名(即使与域名对应的ip)等任意一个不同,就构成跨域。
链接 | 说明 | 同源检测 |
---|---|---|
http://www.servicea.com/first.do http://www.servicea.com:8080/first.do | 端口不同 | 跨域 |
http://www.servicea.com/first.do https://www.servicea.com/first.do | 协议不同 | 跨域 |
http://www.servicea.com/first.do http://www.serviceb.com/first.do | 域名不同 | 跨域 |
http://www.servicea.com/first.do http://10.28.10.13/first.do | 域名和其对应的ip | 跨域 |
http://www.servicea.com/first.do http://img.servicea.com/first.do | 子域不同 | 跨域 |
http://www.servicea.com/first.do http://www.servicea.com/second.do | 同域名不同方法 | 不跨域 |
http://www.servicea.com/first.do http://www.servicea.com/web/first.do | 同域名不同文件夹 | 不跨域 |
三、什么是JSONP(JSON With Padding)?
动态添加<script>来调用服务器提供的js脚本。
这是一种非正式的传输方式,由于发现js文件不受同源策略的影响,包括含有src的标签都不受影响。所以通过web动态添加<script>,通过src调用服务器同时带上一个callback的参数,服务器创建一个包裹返回数据的函数,函数名就是callback的参数,最终返回一个可执行的js函数调用。
四、如何使用JSONP解决跨域问题?
现有如下两个服务器:A: http://localhost:8080 和B:http://localhost:8888,浏览器访问地址:
http://localhost:8080/mybatis/jsp/test.jsp
1、正常ajax访问,出现的跨域问题
访问 http://localhost:8080/mybatis/home
前端代码:
$.ajax({
url : 'http://localhost:8080/mybatis/home',
type : 'get',
data : {
name : "8080"
},
success : function(resp) {
console.log(resp);
}
});
服务器:
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String name = req.getParameter("name");
resp.getWriter().append("welcome "+ name + " !");
}
结果:正常访问
访问http://localhost:8888/mybatis/home
前端代码:
$.ajax({
url : 'http://localhost:8888/mybatis/home',
type : 'get',
data : {
name : "8888"
},
success : function(resp) {
console.log(resp);
}
});
服务器:
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String name = req.getParameter("name");
resp.getWriter().append("welcome "+ name + " !");
}
结果:不能访问
因为同源策略,端口不同出现跨域访问。
2、动态创建script
访问http://localhost:8888/mybatis/home
前端代码:
$("head").append("<script src='http://localhost:8888/mybatis/ScriptServlet?name=script&callback=testscript'><\/script>");
//或者如下js
//var sc = document.createElement("script");
//sc.src = "http://localhost:8888/mybatis/ScriptServlet?name=script&callback=testscript";
//document.body.insertBefore(sc,document.body.firstChild);
function testscript(resp) {
console.log(resp);
}
服务器:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String callback = request.getParameter("callback");
String name = request.getParameter("name");
response.getWriter().append(callback+"(\""+name+"\")");
}
结果:可以访问
通过js创建一个<script>标签,同时在请求的地址中拼接一个callback=testscript的参数;
在服务器中,获取callback的参数值,与返回数据拼装成一个可执行的函数:testscript("+返回的数据+");
返回到web后自动执行testscript函数。
3、jquery的JSONP方式
访问http://localhost:8888/mybatis/home
前端代码:
$.ajax({
url : 'http://localhost:8888/mybatis/ScriptServlet',
type : 'get',
data : {
name : 'test 8888,jsonp'
},
dataType : 'jsonp',
// jsonp:'othercallback', //指定参数名称,即callback改为othercallback,所以后台获取相应也要改为request.getParameter("othercallback")
// jsonpCallback:'othername', //指定回调函数名称
success : function(resp) {
console.log(resp);
}
});
服务器:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String callback = request.getParameter("callback");
String name = request.getParameter("name");
response.getWriter().append(callback+"(\""+name+"\")");
}
结果:可以访问
只需要在ajax方法里,添加一个dataType:'jsonp'参数即可。乍一看,咋没有回调函数的名称,其实查看url就知道了,
4、jsonp全部参数
访问http://localhost:8888/mybatis/home
前端代码:
$.ajax({
url : 'http://localhost:8888/mybatis/ScriptServlet',
type : 'get',
data : {
name : 'test 8888,jsonp'
},
dataType : 'jsonp',
jsonp:'othercallback', //指定参数名称,即callback改为othercallback,所以后台获取相应也要改为request.getParameter("othercallback")
jsonpCallback:'othername', //指定回调函数名称
success : function(resp) {
console.log("ajax:"+resp);
}
});
function othername(resp){
console.log("othername:"+resp);
}
服务器:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String callback = request.getParameter("othercallback");
String name = request.getParameter("name");
response.getWriter().append(callback+"(\""+name+"\")");
}
结果:可以访问
返回web后先执行了othername函数,最后执行success。
5、使用getJSON
访问http://localhost:8888/mybatis/home
前端代码:
$.getJSON("http://localhost:8888/mybatis/ScriptServlet?name=getJSON&othercallback=?",function(resp){
console.log(resp);
});
服务器:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String callback = request.getParameter("othercallback");
String name = request.getParameter("name");
response.getWriter().append(callback+"(\""+name+"\")");
}
结果:可以访问
6、前端全部代码
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>This is 8080 page</title>
</head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js">
</script>
<script type="text/javascript">
$(function() {
$("a").click(function() {
switch ($(this).attr("id")) {
case "1":
$.ajax({
url : 'http://localhost:8080/mybatis/home',
type : 'get',
data : {
name : "8080"
},
success : function(resp) {
console.log(resp);
}
});
break;
case "2":
$.ajax({
url : 'http://localhost:8888/mybatis/home',
type : 'get',
data : {
name : "8888"
},
success : function(resp) {
console.log(resp);
}
});
break;
case "3":
$("head").append("<script src='http://localhost:8888/mybatis/ScriptServlet?name=script&callback=testscript'><\/script>");
// var sc = document.createElement("script");
// sc.src = "http://localhost:8888/mybatis/ScriptServlet?name=script&callback=testscript";
// document.body.insertBefore(sc,document.body.firstChild);
break;
case "4":
$.ajax({
url : 'http://localhost:8888/mybatis/ScriptServlet',
type : 'get',
data : {
name : 'test 8888,jsonp'
},
dataType : 'jsonp',
// jsonp:'othercallback', //指定参数名称,即callback改为othercallback,所以后台获取相应也要改为request.getParameter("othercallback")
// jsonpCallback:'othername', //指定回调函数名称
success : function(resp) {
console.log(resp);
}
});
break;
case "5":
$.ajax({
url : 'http://localhost:8888/mybatis/ScriptServlet',
type : 'get',
data : {
name : 'test 8888,jsonp'
},
dataType : 'jsonp',
jsonp:'othercallback', //指定参数名称,即callback改为othercallback,所以后台获取相应也要改为request.getParameter("othercallback")
jsonpCallback:'othername', //指定回调函数名称
success : function(resp) {
console.log("ajax:"+resp);
}
});
break;
case "6":
$.getJSON("http://localhost:8888/mybatis/ScriptServlet?name=getJSON&othercallback=?",function(resp){
console.log(resp);
});
default:
break;
}
});
});
function othername(resp){
console.log("othername:"+resp);
}
function testscript(resp) {
console.log(resp);
}
</script>
<body>
<a href="javascript:void(0)" id="1">访问本homeservlet</a>
<br />
<a href="javascript:void(0)" id="2">访问8888 homeservlet</a>
<br />
<br />
<a href="javascript:void(0)" id="3">动态创建script</a>
<br>
<a href="javascript:void(0)" id="4">jquery跨域请求</a>
<br>
<a href="javascript:void(0)" id="5">jquery跨域请求(自定义callback)</a>
<br>
<a href="javascript:void(0)" id="6">getJSON</a>
<br>
</body>
</html>
五、JSONP主要弊端,缺陷
1、不支持post方式,即使在ajax中改成post,实际也是get方法
将以上ajax中的get改成post:
$.ajax({
url : 'http://localhost:8888/mybatis/ScriptServlet',
type : 'post',
data : {
name : 'test 8888,jsonp'
},
dataType : 'jsonp',
// jsonp:'othercallback', //指定参数名称,即callback改为othercallback,所以后台获取相应也要改为request.getParameter("othercallback")
// jsonpCallback:'othername', //指定回调函数名称
success : function(resp) {
console.log(resp);
}
});
看起来请求成功了,但是实际最终还是get请求,
因为jsonp是通过动态创建script,而动态生成script的src只能用get。
2、JSONP是一种脚本注入,存在一定的安全隐患。
安全问题可以参考:https://blog.csdn.net/u014021893/article/details/72303110