跨域是指从一个域名的网页去请求另一个域名的资源。比如从www.baidu.com 页面去请求 www.google.com 的资源。跨域的严格一点的定义是:只要 协议,域名,端口有任何一个的不同,就被当作是跨域,并有以下注意事项:
1、表单默认提交(跳转页面或刷新页面)、超链接访问域外的资源,这是允许的,因为在点击按钮/超链接时,浏览器地址已经变了,这就是一个普通的请求,不属于跨域;
2、ajax(借助xmlhttprequest)跨域请求,这是被禁止的,因为ajax就是为了接受接受响应,这违背了,不允许跨域读的原则;
3、jsonp属于跨域读且形式限制为GET方式,它利用了script标签的特性;这是允许的。因为浏览器把跨域读脚本,当作例外,类似的img、iframe的src都可以请求域外资源;
4、出现Access control allow origin错误,说明是跨域请求失败!浏览器发送请求成功,同时浏览器也接收到响应了,但是限制了XmlHttpRquest接收请求,不会让xmlhttprequest接受到响应,并且在js控制台报错。这也就是我们在网络控制台(Network)能看见http 状态码是200,但是在js控制台(Console)出现js错误的原因。
跨域访问的几种方式
1. 通过jsonp方式进行跨域(仅限get请求):
(1)前端jquery的jsonp方式;
(2)前端AngularJS的jsonp方式;
(3)手动实现jsonp;
jsonp方式进行跨域访问的原理:
其本质是利用了标签具有可跨域的特性,由服务端返回预先定义好的javascript函数的调用,并且将服务端数据以该函数参数的形式传递过来。
JSONP技术和Ajax没有关系。
我们知道<script>标签可以加载跨域的javascript脚本,并且被加载的脚本和当前文档属于同一个域。
因此在文档中可以调用/访问脚本中的数据和函数。
如果javascript脚本中的数据是动态生成的,那么只要在文档中动态创建<script>标签就可以实现和服务端的数据交互。
JSONP就是利用<script>标签的跨域能力实现跨域数据的访问,请求动态生成的JavaScript脚本同时带一个callback函数名作为参数。
其中callback函数本地文档的JavaScript函数,服务器端动态生成的脚本会产生数据,并在代码中以产生的数据为参数调用callback函数。当这段脚本加载到本地文档时,callback函数就被调用。
代码如下:
(1)前端jquery的jsonp方式:
function crossDomainJsonp() {
$.ajax({
type:"get",
url:"http://192.168.2.23/pdf/crossDomeJsonp1.do",
async:true,
data:{},
dataType: "jsonp", //返回类型为jsonp,实现跨域
jsonp:"callback", //jsonp和jsonpCallBack相当于在url后添加一个参数:?callback=back
jsonpCallback:"back", //设定回调函数的名字,传到后台,进行包装,不设定自动生成
success: function(data) { //成功执行处理,对应后台返回的back(data)方法
alert(data);
console.log(data);
}
});
}
后台代码:
/**
*
* @Title: crossDomeJsonp1
* @Description: TODO(jsonp方式一)
* @return MappingJacksonValue 返回类型
* @param callback
* @return
*/
@RequestMapping(value="crossDomainJsonp1", method=RequestMethod.GET)
@ResponseBody
public MappingJacksonValue crossDomainJsonp1(String callback) {
Map<String, Object> resultMap = new HashMap<>();
try {
resultMap.put("success", true);
} catch (IllegalStateException e) {
e.printStackTrace();
resultMap.put("success", false);
}
//包装jsonp
MappingJacksonValue jacksonValue = new MappingJacksonValue(resultMap);
//设置包装的回调方法名
jacksonValue.setJsonpFunction(callback);
return jacksonValue;
}
/**
*
* @Title: crossDomeJsonp2
* @Description: TODO(jsonp方式二)
* @return String 返回类型
* @param callback
* @return
*/
@RequestMapping(value="crossDomainJsonp2", method=RequestMethod.GET, produces=MediaType.APPLICATION_JSON_VALUE + ";charset=utf-8")
@ResponseBody
public String crossDomainJsonp2(String callback) {
Map<String, Object> resultMap = new HashMap<>();
try {
resultMap.put("success", true);
} catch (IllegalStateException e) {
e.printStackTrace();
resultMap.put("success", false);
}
//把对象转换成json数据(自定义的JSONUtils)
String jsonResult = JSONUtils.objectToJson(resultMap);
//拼接字符串
String resultStr = callback + "(" + jsonResult + ");";
return resultStr;
}
(2)前端AngularJS的jsonp方式:AngularJS的$http 也提供了对jsonp的访问,直接调用jsonp进行跨域访问
$http.jsonp('http://192.168.2.23/pdf/crossDomeJsonp1.do?callback=back')
.success(function(data) {
alert(data);
console.log(data);
}).error(function(err) {
alert('error:' + err);
});
后台同上
(3)手动实现jsonp
jsonp的本质而都是通过加载javascript的方式来做的,所以如果项目没有依赖jQuery或者AngularJS,则可以自己手动实现jsonp的调用。
原理很简单,就是用javascript动态加载一个script文件,同时定义一个callback函数给script执行而已。
//定义callback 函数
function back1(data) {
alert(data);
console.log(data);
}
//通过添加script标签,进而执行js(执行完成应该删除生成的script)
function crossDomain2() {
//创建并加载script
var script = document.createElement('script');
script.src = 'http://192.168.2.23/pdf/crossDomeJsonp1.do?callback=back1';
document.body.appendChild(script);
}
2. 服务器端设置请求头Access-Control-Allow-Origin实现跨域
传统的跨域请求没有好的解决方案,无非就是jsonp和iframe,随着跨域请求的应用越来越多,W3C提供了跨域请求的标准方案(Cross-Origin Resource Sharing)。
IE8、Firefox 3.5 及其以后的版本、Chrome浏览器、Safari 4 等已经实现了 Cross-Origin Resource Sharing 规范,实现了跨域请求。
在服务器响应客户端的时候,带上Access-Control-Allow-Origin头信息。
• 如果设置 Access-Control-Allow-Origin:*,则允许所有域名的脚本访问该资源。
• Access-Control-Allow-Origin:http://www.phpddt.com.com,允许特定的域名访问。
前端:正常的ajax请求
后台:
/**
*
* @Title: crossDome1
* @Description: TODO(通过设置响应头Access-Control-Allow-Origin解决跨域请求)
* @return Map<String,Object> 返回类型
* @param response
* @return
*/
@RequestMapping(value="crossDomain1", method=RequestMethod.GET)
@ResponseBody
public Map<String, Object> crossDomain1(HttpServletResponse response) {
Map<String, Object> resultMap = new HashMap<>();
try {
//利用Access-Control-Allow-Origin响应头解决跨域请求
// response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Origin", "http://127.0.0.1:8020");
resultMap.put("success", true);
} catch (IllegalStateException e) {
e.printStackTrace();
resultMap.put("success", false);
}
return resultMap;
}