什么是跨域?跨域,指的是浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器施加的安全限制。
造成跨域的三个原因:1.浏览器限制,2.浏览器同源策略,3.发送的请求是xmq(XmlHttpRequest)请求。
解决思路:
1.通过指定参数让浏览器不去做这个限制。
2.改变请求方法,使用jsonp代替xhq请求。jsonp会动态创建一个script,在script中发送请求。
3.跨域。由于jsonp有很多限制无法满足我们开发当中的请求,所以我们经常采用这种去解决跨域。方法1---被调用方修改:让被调用方修改代码支持跨域。举例:a域名向b域名发出请求的时候,b域名返回信息中增加一些字段告诉浏览器我支持a域名跨域。方法2---调用方修改:通过一个代理http服务器向浏览器隐藏跨域。举例:a域名向b域名发出请求时候,通过代理转化为b域名向b域名发送请求。
解决方法:
1.禁止浏览器检查是否跨域。google浏览器为例,以命令行参数启动(打开google浏览器安装位置,按住shift右键google浏览器》在此处打开命令窗口),然后输入命令chrome -disable-web-security --user-data-dir=e:test(这里e:test必须为你的电脑上其中一个真实路径,具体作用还不清楚)。然后会新启动一个google浏览器,在该浏览器中跨域不会被检查,跨域访问也得到了解决。
2.jsonp解决方法。注意该方法后台需要改动,因为jsonp通过动态创建script提交申请返回js,而后台一般默认返回json数据。jsonp提交默认约定提交一个callback函数,后台的返回数据也会封装在callback函数中。前端提交jsonp申请,我们需要在后台修改代码告诉后台带有callback函数的是jsonp请求(不同框架实现方法不一样)。如果想把callback改成别的名字,只需要在前台修改jsonp提交的函数名,具体可百度。
弊端:
a.后台代码需要改动,如果没有权限去修改就不能用jsonp。
b.只支持get方法(通过动态创建script发出请求,而该方法是get类型)。
c.发送的不是XmlHttpRequest请求。而xhr请求中有很多新的特性,比如异步和事件等。
3.跨域。
(1).被调用方解决跨域。思路是a域向b域的htttp服务器发送请求,修改被调用方的http服务器,在响应头里面加入一些字段告诉a域的浏览器我运行跨域。
具体方法:
a.解决方案1:在后台写一个过滤器。
public class CrosFilter implements Filter{
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// TODO Auto-generated method stub
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest req=(HttpServletRequest)request;
HttpServletResponse res=(HttpServletResponse)response;
String origin=req.getHeader("Origin"); //跨域时候会在请求头中增加一个Origin字段
if(!StringUtils.isEmpty(origin)) { //这个方法可以解决带cookie请求不能用*匹配又可以增加别的域
res.addHeader("Access-Control-Allow-Origin", origin);
}
String headers=req.getHeader("Access-Control-Request-Headers");
//支持所有自定义头
if(!StringUtils.isEmpty(headers)) {
res.addHeader("Access-Control-Request-Headers", headers);
}
//以下方法是针对简单请求,浏览器执行顺序是先先执行后判断。
//res.addHeader("Access-Control-Allow-Origin", "http://127.0.0.1:8083"); //告诉浏览器支持这个域的跨域
//res.addHeader("Access-Control-Allow-Origin", "*");
res.addHeader("Access-Control-Allow-Methods", "GET"); //支持跨域的方法
//res.addHeader("Access-Control-Allow-Methods", "*");
System.out.println("k*********************************************跨域");
//以下方法是针对非简单请求,浏览器执行顺序是先判断后执行。区别是,先判断为request中会有一个字段默认不被服务器允许任何报错。
//因此我们需要在服务器中增加一个头告诉浏览器我支持这个header
//res.addHeader("Access-Control-Allow-Headers", "Content-Type,x-header1,x-header2");
//非简单请求第一次会预检命令,看服务器是否允许head中的字段,如果允许第二次发送请求,会影响效率。所以我们可以让浏览器缓存服务器的预检命令,这样第二次不会发送预检命令请求.
res.addHeader("Access-Control-Max-Age", "3600");
//带cookie请求,带cookie必须全匹配不能带*
res.addHeader("Access-Control-Allow-Credentials", "true");
chain.doFilter(request, response);
}
@Override
public void destroy() {
// TODO Auto-generated method stub
}
}
该方法会向响应头中增加相应的信息来告诉浏览器我支持这个跨域。
前端代码:
<script>
//每一个测试用例的超时时间,告诉jasmine多久可以完成
jasmine.DEFAULT_TIMEOUT_INTERVAL=1000;
//请求的接口的前缀
var base="http://127.0.0.1:8089/test";
//var base="/ajaxserver";
//测试模块
describe("晓风轻-ajax跨域完全讲解",function(){
//ge
it("get1请求",function(done){ //此处跨域请求会失败
//服务器返回的结果
var result;
$.getJSON(base+"/get1").then(function(jsonObj){
result=jsonObj;
})
//由于是异步请求,需要使用setTime来校验
setTimeout(function(){
expect(result).toEqual({ //把返回结果和测试结果进行对比,一致返回true
"data":"get1.ok"
});
//校验完成,通知jasmine框架
done();
},100);
});
//jsonp跨域请求
it("jsoup请求",function(done){
//服务器返回的结果
var result;
$.ajax({
url:base+"/get1",
dataType:"jsonp",
jsonp: "callback",
success:function(json){
result=json;
}
});
//由于是异步请求,需要使用setTime来校验
setTimeout(function(){
expect(result).toEqual({ //把返回结果和测试结果进行对比,一致返回true
"data":"get1.ok"
});
//校验完成,通知jasmine框架
done();
},100);
});
//非简单方式跨域请求
it("jsoup请求",function(done){
//服务器返回的结果
var result;
$.ajax({
type:"post",
url:base+"/postJson",
contentType:"application/json;charset=utf-8",
data:JSON.stringify({userName:"xiaofengqing"}),
success:function(json){
result=json;
}
});
//由于是异步请求,需要使用setTime来校验
setTimeout(function(){
expect(result).toEqual({ //把返回结果和测试结果进行对比,一致返回true
"data":"postJson xiaofengqing"
});
//校验完成,通知jasmine框架
done();
},100);
});
//带Cookie的跨域请求
/** it("getCookie请求",function(done){
//服务器返回的结果
var result;
$.ajax({
type:"post",
url:base+"/getCookie",
xhrFields:{ //这个请求表示ajax请求会带上cookie
withCredential:true
},
success:function(json){
result=json;
}
});
//由于是异步请求,需要使用setTime来校验
setTimeout(function(){
expect(result).toEqual({ //把返回结果和测试结果进行对比,一致返回true
"data":"getCookie AAA BBB"
});
//校验完成,通知jasmine框架
done();
},100);
});*/
//带自定义头的跨域请求
it("getHeader请求",function(done){
//服务器返回的结果
var result;
$.ajax({
type:"get",
url:base+"/getHeader",
headers:{ //ajax请求增加自定义头方法1
"x-header1":"AAA"
},
beforeSend:function(xhr){ //ajax请求增加自定义头方法2
xhr.setRequestHeader("x-header2","BBB")
},
success:function(json){
result=json;
}
});
//由于是异步请求,需要使用setTime来校验
setTimeout(function(){
expect(result).toEqual({ //把返回结果和测试结果进行对比,一致返回true
"data":"getHeader AAA BBB"
});
//校验完成,通知jasmine框架
done();
},100);
});
});
</script>
这里我们采用的是jasmine测试框架,用法可以百度自行了解。
b.解决方法2:通过nginx配置。我们安装一个nginx服务器,然后在hosts文件中增加
127.0.0.1 b.com
可以在控制台窗口通过命令行语句ping b.com来测试是否配置成功b.com域名。
接着我们来配置nginx服务器,我们在nginx安装路径nginx-1.14.0\conf下的nginx.conf文件尾加入以下代码
include vhost/*.conf;#载入这个目录下所有conf文件
然后在nginx-1.14.0\conf\vhost路径下增加一个b.com.conf文件并添加以下代码
server{
listen 80; #监听的端口
server_name b.com; #首先在hosts配置域名本地解析www.b.com 然后配置nginx监听的域名
location /{
proxy_pass http://localhost:8089/; #被调用方的端口是8089,这句话表示把所有的请求都转到localhost
#nginx中请求头必须小写,-必须变成_
add_header Access-Control-Allow-Methods *; #向响应头中增加固定的命令
add_header Access-Control-Max-Age 3600;
add_header Access-Control-Allow-Credentials true;
add_header Access-Control-Allow-Origin $http_origin; #从request中动态取出header并向响应头中增加
add_header Access-Control-Allow-Headers $http_access_control_request_headers;
if ($request_method = OPTIONS){ #如果是预检命令,不向应用服务器提交直接返回true
return 200;
}
}
}
把上面的前端代码中的base变量修改为b.com域名如下
var base="http://b.com/test";
此时跨域就解决了。
注:apache解决方法类似nginx,可以自行查询资料。
(2)调用方解决方案:修改调用方的http服务器,a域通过a域的http服务器向b域的http服务器发送请求来隐藏跨域。
首先我们在hosts文件中添加以下代码
127.0.0.1 a.com
然后在nginx-1.14.0\conf\vhost路径下增加一个a.com.conf文件并添加以下代码
server{
listen 80; #监听的端口
server_name a.com; #首先在hosts配置域名本地解析www.b.com 然后配置nginx监听的域名
location /{
proxy_pass http://localhost:8083/; #调用方的端口是8089,这句话表示把所有的请求都转到localhost
}
location /ajaxserver{
proxy_pass http://localhost:8089/test/; #被调用方
}
}
接着在前台文件中修改base变量
var base="/ajaxserver";
此时跨域就已经解决了。
(3)如果我们用的是Spring框架我们只需要加入@CrossOrigin注解就可以解决跨域。该注解加在方法上表示该方法支持跨域,该方法加载类上表示该类左右方法支持跨域。