跨域,只看这篇就够了!

读完这篇跨域,大抵心下清楚其他跨域的不必看罢了。   ---鲁迅

目录

 

一、什么时候出现跨域

二、跨域的时候究竟发生了什么?我们怎么解决?

 三、隐藏的秘密

四、jsonp


 

一、什么时候出现跨域

每个web开发者都会遇到跨域。他们或多或少会知道大概类似于下面这样的知识:

如果请求来源和请求目标的不在同一个服务器上会发生跨域。

但跨域的原因仅此于此吗?先看一个现象。

服务器就一个url

@RequestMapping("")
public String index (){
	return "ok";
}

然后我们开始测试,1号测试,在postman里输入请求:localhost:8080。结果得到了一个ok,并没有报什么跨域的错误。

那么仅此一个例子就可以判定上面的那个知识是错误的,因为我在postman里直接输入的话,并没有请求来源,但是有请求目标,他们是不一致的。 

但我觉得可以换个方案,2号测试,写一个html来访问试图去迎合上面那个知识,注意,这个html就直接放本地盘符啊,不要和服务器代码放一起,否则他们就是在同一个服务器上了,自然不好引起跨域。

<html>
	<script src="jquery.js"></script>
	<script>
		$.post("http://localhost:8080",{},function(data){
			console.log(data)
		})
	</script>
<html>

本地直接打开后,出现了跨域的问题。

竟然请求失败了,请求来源和目标不一致,出现了跨域问题。

为什么用postman可以请求,而使用html就出现跨域问题呢?

很简单。跨域问题出现的其中一个要素就是:跨域是浏览器里才会出现。跨域本身就是浏览器为了解决一些安全问题而设置的警告拦截。

那么浏览器里的来源目标不一致的请求都会产生跨域吗?

我们看下3号测试在浏览器地址里输入:localhost:8080.

咦?竟然不跨域了?

4号测试,我们写个a标签试下,这个a标签的来源可以是任何地址。

<a href="http://localhost:8080">点我</a>

也不跨域。

跨域问题出现的第二个要素是:只有js的xhr才会发生跨域,html标签并不会,包括不仅限于a标签,form标签等。

js跨域,实际上就意味着ajax,vue等js框架同样会有跨域。

跨域问题还有一个要素,当然,就是上面那个不完整的常见知识,我们说的再精准一些:来源和目标的协议、主机、端口有任意一个不同

只有以上三点同时出现,才会引出跨域问题,再重申一遍:

跨域三要素:

浏览器、js、目标来源不一致。

 

二、跨域的时候究竟发生了什么?我们怎么解决?

我们在上面2号测试的时候,浏览器报错下面:

Access to XMLHttpRequest at 'http://localhost:8080/' from origin 'null' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

它意思是说,你的目标是http://localhost:8080/,但是来源是null.这不一致,说不定是偷渡,所以我要检查一番,看看服务器给你开发票了没用,结果发现返回的响应头里没“Access-Control-Allow-Origin”这张发票,所以违法。

Access-Control-Allow-Origin就是允许的来源。看来我要想解决它。必须在服务器设置下。

5号测试,服务器写个filter,加入这行代码

response.setHeader("Access-Control-Allow-Origin", "123");

 运行一下,又报错跨域了。跟上次一样

Access to XMLHttpRequest at 'http://localhost:8080/' from origin 'null' has been blocked by CORS policy: The 'Access-Control-Allow-Origin' header contains the invalid value '123'.

 仔细一看,还有点不一样,这段意思是,这张发票是开给123的,而你是“null”,所以也不合法。

那就6号测试

response.setHeader("Access-Control-Allow-Origin", "null");

 运行起来,这次没报错了,

那么解决跨域的一个操作就是:

服务端设置允许来源Access-Control-Allow-Origin,如果希望省事,那么可以允许任何来源

response.setHeader("Access-Control-Allow-Origin", "*");

如果只是这样就好了,但是我们最好加一些难度,比如,7号测试,添加一个token来看看会是怎么样的

<html>
	<script src="jquery.js"></script>
	<script>
		$.ajax({
			url:'http://localhost:8080',
			type:'post',
			headers:{ 
                'token':'123456'
            },
			data:{},
			success:function(data){console.log("sucess");},    
        });
	</script>
	
<html>

它又报错跨域错误了,

Access to XMLHttpRequest at 'http://localhost:8080/' from origin 'null' has been blocked by CORS policy: Request header field token is not allowed by Access-Control-Allow-Headers in preflight response.

尽管我英语不是太好,但是对比着上次的错误我大概能猜出来,竟然还需要另一张发票Access-Control-Allow-Headers。

是的,如果你自定义了header,那么同时还需要另外一张发票。

我们修改下服务器返回发票,8号测试

response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Headers", "token,token1,token2");

看,我们开了header是token/token1/token2的发票,你的自定义header要是在其中,没问题。

那么解决跨域的第二个操作就是:

如果请求有自定义header,服务端设置允许来源Access-Control-Allow-Headers,如果希望省事,那么可以允许任何来源

response.setHeader("Access-Control-Allow-Headers", "*");

走到这里,其实大部分跨域问题都已经解决了。但是我们可以更进一步,这次我们设置下修改下method,由get/post方法改成put,9号测试

<html>
	<script src="jquery.js"></script>
	<script>
		$.ajax({
			url:'http://localhost:8080',
			type:'put',
			data:{},
			success:function(data){console.log("sucess");},    
        });
	</script>
	
<html>

它又报错了。

Access to XMLHttpRequest at 'http://localhost:8080/' from origin 'null' has been blocked by CORS policy: Method PUT is not allowed by Access-Control-Allow-Methods in preflight response.

 我闭上眼睛都知道我要做什么了,

如果请求的method比较稀奇古怪,不是get也不是post啊,那也要设置,不过,干脆省事些吧。

response.setHeader("Access-Control-Allow-Methods", "*");

当然还有另一个设置,

如果你需要跨域验证cookie的话。

 response.setHeader("Access-Control-Allow-Credentials", "true")

解决方案总结一下吧:

response.setHeader("Access-Control-Allow-Origin", "*");//允许任何请求来源        
response.setHeader("Access-Control-Allow-Methods", "*");//允许任何method
response.setHeader("Access-Control-Allow-Headers", "*");//允许任何自定义header
response.setHeader("Access-Control-Allow-Credentials", "true");//允许跨域cookie

 三、隐藏的秘密

发生原因和解决方案都已经列出,但跨域还有隐藏的一个秘密,这个就当了解一下吧。

如果我们去抓包2号测试和9号测试,对比就会发现他们的结果完全不同。

2号测试请求可以请求到数据,返回了ok。但是浏览器报错跨域。

9号测试请求,它的真实method竟然不是put,而是option,它无法请求到数据,浏览器报错跨域。

这究竟是为什么呢?

其实跨域的请求,在浏览器看来又分为简单请求和非简单请求。

简单请求浏览器就直接请求,和正常的没什么区别,然后根据返回发票判断是否跨域,如果跨域,就报错,返回来的数据就扔了。

非简单请求浏览器发起一个一模一样但是method是option的测试请求,先去嗅探返回发票,如果发票正确,那就再去请求一次真实数据,如果发票错误,就报错。

浏览器判断简单非简单请求的依据有:

以下简单请求,其他非简单请求:

1.请求方式:GET、POST、HEAD

2.HTTP头部信息不超过一下几种字段:

Accept、Accept-Language、Content-Language、Last-Event-ID、Content-Type(只有三个值application/x-www-form-urlencoded、multipart/form-data、text/plain)

这意味着如果是有自定义header或者contextType=applcaition/json的算是非简单请求

四、jsonp

有网友给的解决方案是ajax请求时,类型改成jsonp,jsonp其实不能算是xhr请求,而是script请求,

localhost:8080/aaa相当于请求localhost:8080/aaa.js,然后再另行处理。

这种方式可以,但也意味着服务端要配合,修改默认返回方式等。也可以。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章