AJAX跨域产生的原因?
1、浏览器限制
2、XHR(XMLHttpRequest)请求
3、跨域(域名不同、端口不同,IP不同)
解决思路
1、让浏览器不做限制,不校验(客户端改动,意义不大)
2、让发出去的请求不是XHR请求(JSONP)
3、 被调用方修改代码,让其支持http跨域的要求
4 、调用方隐藏跨域,通过代理发出去的只向A域名的请求只向B域名
具体解决方法
1、通过浏览器不做限制
命令行参数启动
chrome --disable-web-security --user-data-dir=D:\Temp
2、通过JSONP让发送出去的请求不是XHR请求
JSON是JSON的一种补充使用方式,使用JSONP后台服务器需要改动
JSONP的原理
1、请求Type不一致,普通请求Type为xhr,jsonp请求类型为script,浏览器只校验请求类型为xhr的请求,其他类型不校验
2、返回的类型不一样,普通请求返回的是json对象,jsonp返回的类型的js脚本
JSONP弊端
1、服务器需要改动代码进行支持
2、只支持GET方法
3、发送的不是xhr请求,不支持异步方法
关键代码:
服务器端代码:
Controller
@RestController
@RequestMapping("/test")
public class TestController {
@GetMapping("get1")
public ResultBean get1(){
System.out.println("TestControllerGet1");
return new ResultBean("get1 ok");
}
}
切片JsonpAdvice
@ControllerAdvice
public class JsonpAdvice extends AbstractJsonpResponseBodyAdvice {
public JsonpAdvice() {
super("callback");
}
}
客户端代码:
$.ajax({
url:base+"/get1",
dataType:"jsonp",
success:function (json) {
result =json;
}
});
3、被调用方解决
跨域请求是直接从浏览器发送到被调用方服务器
请求被分为简单请求和非简单请求
简单请求先执行后判断:
方法为:GET、HEAD、POST
请求header里面:
无自定义头
Content-Type为一下几种:
text/plain
multipart/form-data
application/x-www-form-urlencoded
非简单请求先判断后执行:
方法为:PUT、DELETE的ajax请求
发送json格式的ajax请求
带自定义头的ajax请求
OPTIONS预检命令缓存
非简单请求每次都会发送两次请求,影响响应效率
res.addHeader("Access-Control-MAX-Age","3600");
表示在一个小时内缓存预检信息,不需要在发送预检命令
服务器实现:
被调用方-Filter上增加过滤
通过对比Origin进行判断是否跨域
res.addHeader("Access-Control-Allow-Origin","*");
res.addHeader("Access-Control-Allow-Methods","*");
res.addHeader("Access-Control-Allow-Headers","*");
带Cookie的跨域
客户端ajax请求带Cookie,只需要在ajax请求中加入
xhrFields:{
withCredentials:true
}
在带有Cookie的请求中:
必须启动cookie,res.addHeader("Access-Control-Allow-Credentials","true");
不能给res.addHeader("Access-Control-Allow-Origin","*");赋值*,必须指定请求的Origin
String origin = req.getHeader("Origin");
if (!StringUtils.isEmpty(origin)){
res.addHeader("Access-Control-Allow-Origin",origin);
}
带自定义头的跨域
不能给res.addHeader("Access-Control-Allow-Headers","*");写死
String headers = req.getHeader("Access-Control-Allow-Headers");
if (!StringUtils.isEmpty(headers)){
res.addHeader("Access-Control-Allow-Headers",origin);
}
关键代码:
DemoApplication启动类中加入Filter过滤器
@Bean
public FilterRegistrationBean registerFilter(){
FilterRegistrationBean bean = new FilterRegistrationBean();
bean.addUrlPatterns("/*");
bean.setFilter(new CrosFilter());
return bean;
}
实现CrosFilter类
public class CrosFilter implements javax.servlet.Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletResponse res = (HttpServletResponse)servletResponse;
HttpServletRequest req = (HttpServletRequest) servletRequest;
String origin = req.getHeader("Origin");
if (!StringUtils.isEmpty(origin)){
res.addHeader("Access-Control-Allow-Origin",origin);
}
res.addHeader("Access-Control-Allow-Origin","*");
res.addHeader("Access-Control-Allow-Methods","*");
res.addHeader("Access-Control-Allow-Headers","*");
res.addHeader("Access-Control-MAX-Age","3600");
res.addHeader("Access-Control-Allow-Credentials","true");
filterChain.doFilter(servletRequest,res);
}
@Override
public void destroy() {}
}
客户端代码:
$.ajax({
type : "post",
url: base + "/postJson",
contentType : "application/json;charset=utf-8",
data: JSON.stringify({name: "xiaofengqing"}),
success: function(json){
result = json;
}
});
被调用方-Nginx配置
nginx增加虚拟主机:在nginx.conf 最后添加 include vhost/*.conf;
新建vhost目录在目录中新建一个配置文件
客户端调用
将客户端ajax请求指向nginx服务器即可,如上:b.com
被调用方-apache配置
打开apache.conf配置文件
开启虚拟主机配置:vhost_alias_module
打开虚拟主机配置文件:#Virtual hosts
开启proxy代理模块:proxy_module
开启proxy_http代理模块:proxy_http_module
开启Header模块:headers_module
开启rewrite模块:rewrite_module
打开虚拟主机的配置文件vhost.conf
客户端调用
将客户端ajax请求指向nginx服务器即可,如上:b.com
被调用方-spring框架解决方案
增加Controller中@CrossOrigin注解即可
@SpringBootApplication
@CrossOrigin
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
4、调用方解决
基于隐藏跨域请求,跨域请求不会直接发送到被调用发服务器,而是通过nginx/apache服务器转发过去(在无法修改服务器端代码的情况下使用)
调用方解决-NGINX
调用方解决-APACHE