1、产生跨域的原因
1.1 浏览器限制
1.2 跨域,发出去的请求不是本域的;
在协议、域名、端口任意一个不一样,浏览器就认为是跨域;
1.3 XHR/XMLHttpRequest请求
如果发送的不是XMLHttpRequest请求,就算是跨域,浏览器也不会存在跨域问题;
2、解决思路
2.1 绕过浏览器限制
指定参数,不让浏览器校验即可解决;意义不大,客户端需要改动;
在Chrome启动程序路径下运行如下命令:
chrome —disable-web-security —user-data-dir=g:\temp3
2.2 修改发送的请求类型不是XHR类型 - JSONP
前台发送ajax请求时,修改dataType为jsonp格式;
$.ajax({
url : url,
dataType : "jsonp",
success : function(data) {
result = data;
}
});
后台
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");
}
}
jsonp发送的是类型是script,返回值类型也是“application/javascript”,并且发送请求时get参数是callback,与后台构造方法中的字符串一致,约定的内容必须一致才可以;
jsonp缺点:a)服务器需要修改代码支持;b)只支持GET请求;c)发送的是js而不是XHR;
2.3 解决跨域问题
2.3.1 被调用方修改代码;
注册并增加过滤器,在response的头文件中添加http允许跨域访问的内容,从而实现跨域访问;
2.3.1.1 服务器端基本实现
注册Filter
@Bean
public FilterRegistrationBean<Filter> registerFilter() {
FilterRegistrationBean<Filter> bean = new FilterRegistrationBean<>();
bean.addUrlPatterns("/*");
bean.setFilter(new CrosFilter());
return bean ;
}
在响应头文件中添加相应内容
public class CrossFilter implements Filter {
@Override
public void destroy() {}
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain filter)
throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;
response.addHeader("Access-Control-Allow-Origin", "http://localhost:8081");
response.addHeader("Access-Control-Allow-Methods", "GET");
filter.doFilter(req, response);
}
@Override
public void init(FilterConfig arg0) throws ServletException {}
}
2.3.1.2 浏览器对于简单请求是先执行后判断;
而对于非简单请求,浏览器先发送OPTIONS预检命令,成功后再发送真正数据;
常见的【简单请求】包括:
|- 方法为:GET/HEAD/POST
|- 请求header里面无自定义头
|-Content-Type为以下几种:
|- text/plain
|- multipart/form-data
|- application/x-www-form-urlencoded
常见的【非简单请求】包括:
|- PUT/DELETE方法的ajax请求;
|- 发送json格式的ajax请求;
|- 带自定义头的ajax请求;
var result;
$.ajax({
url : base + "/postJson",
contentType : "application/json;charset=utf-8",
type : "post",
data : JSON.stringify({name:"zhangsan"}),
success : function(json) {
result = json;
}
});
非简单请求每次都发送OPTIONS预检命令影响效率,在HTTP协议里增加一个响应头来缓存预检命令:Access-Control-Max-Age对应缓存时间单位是秒;
response.addHeader("Access-Control-Max-Age", "3600");
2.3.1.3 带cookie的跨域
Access-Control-Allow-Origin不能为*,必须是全匹配(完整的域名);
Access-Control-Allow-Origin必须为true;
从request中获取“Origin”,即完整的域名,设置到“Access-Control-Allow-Origin”中;
发送的cookie是被调用方域名的cookie,因为不可能跨域获取cookie;
document.cookie = 'cookie1=lisi'
var result;
$.ajax({
url : base + "/getCookie",
type : "get",
xhrFields:{
withCredentials:true
},
success : function(json) {
result = json;
}
});
String origin = request.getHeader("Origin");
if (!StringUtils.isEmpty(origin)) {
response.addHeader("Access-Control-Allow-Origin", origin);
}
response.addHeader("Access-Control-Allow-Credentials", "true");
2.3.1.4 带自定义头的跨域
var result;
$.ajax({
url : base + "/getHeader",
type : "get",
headers:{
"x-header1":"AAA"
},
beforeSend:function(xhr) {
xhr.setRequestHeader("x-header2","BBB")
},
success : function(json) {
result = json;
}
});
String origin = request.getHeader("Origin");
if (!StringUtils.isEmpty(origin)) {
response.addHeader("Access-Control-Allow-Origin", origin);
}
String headers = request.getHeader("Access-Control-Request-Headers");
if (!StringUtils.isEmpty(headers)) {
response.addHeader("Access-Control-Allow-Headers", headers);
}
response.addHeader("Access-Control-Allow-Methods", "*");
2.3.1.5 Spring框架
注释注册Filter的配置代码,只需要增加@CrossOrigin即可;
如果是带Cookie的跨域,需要添加“allowCredentials="true"”的属性,因为默认配置为false,会报错;
2.3.2 调用方修改代码 - 隐藏跨域;
反向代理:指向同一个域名的不同URL
使用反向代理,把需要跨域调用的接口代理为本域的一个接口,让浏览器认为是调用的本域接口,以实现隐藏跨域;