跨域问题

什么是跨域?跨域,指的是浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器施加的安全限制。

造成跨域的三个原因: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注解就可以解决跨域。该注解加在方法上表示该方法支持跨域,该方法加载类上表示该类左右方法支持跨域。



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值