跨域问题简单的说就是前台请求一个后台链接,发送请求的前台与后台的地址不在同一个域下,就会产生跨域问题。这里所指的域包括协议、IP地址、端口等。
1.跨域访问安全问题
后端代码:
package cn.qs.controller;
import java.util.LinkedHashMap;
import java.util.Map;
import org.apache.commons.collections.MapUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RequestMapping("/test")
@RestController
public class TestController {
@GetMapping("/get")
public Map<String, Object> get(@RequestParam Map<String, Object> condition) {
if (MapUtils.isEmpty(condition)) {
condition = new LinkedHashMap<>();
condition.put("param", null);
}
return condition;
}
}
前端代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<script type="text/javascript" src="js/jquery-1.8.3.js" ></script>
<body>
</body>
<script>
+ function test() {
$.getJSON("http://localhost:8088/test/get.html", {}, function(res) {
console.log(res);
});
}();
</script>
</html>
结果:虽然后端正常响应,但是JS报错,这就是跨域安全问题,如下:
js报错如下:
Failed to load http://localhost:8088/test/get.html: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://127.0.0.1:8020' is therefore not allowed access.
发生ajax跨域问题的原因:(三个原因同时满足才可能产生跨域问题)
(1)浏览器限制
发生ajax跨域的问题的时候后端是正常执行的,从后台打印的日志可以看出,而且后台也会正常返回数据。浏览器为了安全进行了限制,说白了就是浏览器多管闲事。
(2)跨域:
当协议、域名、端口不一致浏览器就会认为是跨域问题。
(3)XHR(XMLHttpRequest)请求,也就是ajax请求
如果不是ajax请求,不存在跨域问题(这个我们应该可以理解,浏览器直接访问以及a标签跳转等方式都不会产生跨域问题)。
2.解决思路
针对上面三个原因可以对跨域问题进行解决。思路如下:
(1)浏览器端:浏览器允许跨域请求,这个不太现实,我们不可能改每个客户端
(2)XHR请求使用JSONP(JSON with Padding)方式进行方式。它允许在服务器端集成Script tags返回至客户端,通过javascript callback的形式实现跨域访问(这仅仅是JSONP简单的实现形式)。
(3)针对跨域问题解决:
被调用方:也就是服务器端接口,服务器允许跨域。但是如果某些情况服务器端不是我们写的就不可行了。
调用发:也就是JS客户端,隐藏跨域。通常是通过代理的形式隐藏跨域请求,使请求都类似于同一域下发出a标签。
3.浏览器禁止检查-从浏览器层次解决
比如chrom启动的时候设置参数关闭安全检查,如下:
chrome --disable-web-security --user-data-dir=g:/test
设置之后可以正常进行访问,这也进一步证明了跨域问题与后台无关。
4..采用JSONP解决,针对XHR原因
JSONP(JSON with Padding) 是一种变通的方式解决跨域问题。JSONP是一种非官方的协议,双方进行约定一个请求的参数。该协议的一个要点就是允许用户传递一个callback参数给服务端,然后服务端返回数据时会将这个callback参数作为函数名来包裹住JSON数据,这样客户端就可以随意定制自己的函数来自动处理返回数据了。
JSONP发出的请求类型是script,不是XHR请求,所以可以绕过浏览器的检查。JSONP返回的是application/javascript,普通的xhr请求返回的是application/json。
JSONP的原理:通过向界面动态的添加script标签来进行发送请求。script标签会加上callback参数以及_,_是为了防止请求被缓存。
比如我们发送一个请求地址是http://localhost:8088/test/get.html?name=zhangsan&callback=handleCallback&_=123。后端看到有约定的参数callback,就认为是JSONP请求,如果XHR正常请求的响应是{success: true},那么后端会将回传的JSON数据作为参数,callback的值作为方法名,如: handleCallback({success: true}), 并将响应头的Content-Type设为application/javascript,浏览器看到是JS响应,则会执行对应的handleCallback(data)方法。
1.JSONP弊端
(1)服务器端代码需要改动
(2)只支持get方法,由于JSONP原理是通过script标签实现的,所以只能发送get请求
(3)不是XHR异步请求。所以不能使用XHR的一些特性,比如异步等。
2.测试JSONP
后端:增加一个advice
package cn.qlq.aspect;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.AbstractJsonpResponseBodyAdvice;
@ControllerAdvice
public class JsonpAdvice extends AbstractJsonpResponseBodyAdvice {
public JsonpAdvice() {
super("callback");
}
}
前端:采用JSON包装的JSONP请求
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<script type="text/javascript" src="js/jquery-1.8.3.js" ></script>
<body>
</body>
<script>
+ function test() {
$.ajax({
type : "get",
async:false,
url : "http://localhost:8088/test/get.html?name=zhangsan",
dataType : "jsonp",//数据类型为jsonp
jsonp: "callback",//服务端用于接收callback调用的function名的参数
success : function(data){
console.log(data);
},
error:function(){
alert('fail');
}
});
}();
</script>
</html>
结果:
(1)请求是script
请求头:
(2)查看响应数据头和数据:
数据如下:
/**/jQuery18309128178844464243_1575299406254({"name":"zhangsan","callback":"jQuery18309128178844464243_1575299406254","_":"1575299406287"});
补充:JSONP也可以自己定义返回的方法名称,默认是JSON生成的随机字符串
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<script type="text/javascript" src="js/jquery-1.8.3.js" ></script>
<body>
</body>
<script>
var handleJSONPresponse = function (res) {
console.log(1);
console.log(res);
console.log(2);
}
function test() {
$.ajax({
type : "get",
async:fal