跨域,只看這篇就夠了!

讀完這篇跨域,大抵心下清楚其他跨域的不必看罷了。   ---魯迅

目錄

 

一、什麼時候出現跨域

二、跨域的時候究竟發生了什麼?我們怎麼解決?

 三、隱藏的祕密

四、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,然後再另行處理。

這種方式可以,但也意味着服務端要配合,修改默認返回方式等。也可以。

 

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