發表日期:2019年8月15日css
若是你作了一些先後端分離的項目,因爲此時前端所在的服務地址與後端所在的服務地址不同,你可能會遇到一個請求被瀏覽器攔截了的問題,瀏覽器在檢測到當前頁面發起的請求不屬於當前域就會將其攔截,這是由於瀏覽器的「同源策略」。
html
那麼,什麼是同源策略呢?
同源策略用於限制頁面發起不一樣域(源)的請求,用於提升請求的安全性。
若是兩個頁面的協議、端口、IP地址(域名)都相同的話,那麼這兩個頁面就是同源,也就是同一個域。前端舉例:
以http://192.168.10.1:8080/index.html爲對照源,
http://192.168.10.1:8080/auth/login.html與它是同源;
http://192.168.10.1:8181/index.html與它不是同源,由於端口不同;
http://192.168.10.30:8080/index.html與它不是同源,由於IP地址不同。java
有些人會問,既然域不同就會攔截,爲何我用了xxxCDN的css文件,這個請求沒有被攔截呢?
這裏要提一些並非全部的跨域請求都會被攔截的。
1.一般瀏覽器不會攔截一些跨域資源嵌入的請求。
這種所謂的資源嵌入,就是相似於<img>
標籤中的src,<script>
中的src,<link>
中的href這樣的請求,這樣的請求是直接請求資源嵌入到你的頁面中的。因此你使用某個cdn的css文件不會被攔截。【因此有種方式就是經過這種嵌入的方式來進行解決跨域的問題】
2.一般瀏覽器不會攔截一些跨域寫資源的請求。
這種所謂的跨域寫資源,就是所謂的超連接請求,頁面重定向,非XMLHttpRequest方式的表單提交(普通的form表單提交)等等。
3.一般瀏覽器會攔截跨域讀資源的請求。
XMLHttpRequest提交表單,XMLHttpRequest請求資源,【常見於異步操做】web除此以外,要再次強調的是,同源策略是瀏覽器的安全策略。因此若是你直接經過postman這些可以不借助瀏覽器來發http請求的軟件來發請求的話,它是不會攔截你的跨域請求的。spring
一個能夠用於測試的例子:
(當我把下面的html文件部署到一個web服務器(tomcat,apache等)中的時候,此時這個網頁處於的域應該是個人本機地址localhost:80
,此時我根據上述的三個方面來測試瀏覽器是否攔截。結果是隻有最後一個是被攔截的。【不要不部署就直接打開這個頁面,不然的話它只是一個普通的本地文件,而非網絡文件,此時瀏覽器不會認爲這是一個域】)apache
<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>用於測試跨域</title> </head> <body> <!-- 跨域資源嵌入,容許 --> <img src="https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1565884598468&di=bb6ccc7a3280b183e4e13c852bc8353c&imgtype=0&src=http%3A%2F%2Fs04.lmbang.com%2FM00%2FCA%2FDE%2FDpgiA1uPAOKAXmJfAADir1hWa-A750.gif" alt=""> <!-- 跨域資源寫操做,容許 --> <a href="http://www.baidu.com">百度</a> <form action="http://www.baidu.com" method="post" > 用戶名:<input type="text" name="username" value="" placeholder=""> <input type="submit" name="提交" value="提交"> </form> <!-- 跨域資源讀操做,禁止 --> <button onclick="senddata()">XMLHttpRequest請求</button> <script type="text/javascript"> var xmlhttp=new XMLHttpRequest(); function senddata(){ xmlhttp.open("GET","https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1565884598468&di=bb6ccc7a3280b183e4e13c852bc8353c&imgtype=0&src=http%3A%2F%2Fs04.lmbang.com%2FM00%2FCA%2FDE%2FDpgiA1uPAOKAXmJfAADir1hWa-A750.gif",true); xmlhttp.send(); </script> </body> </html>
爲何瀏覽器會使用同源策略?它想解決什麼問題?
首先,先談一下cookie吧,cookie主要用於存儲一些當前網站的一些數據,在一些舊的web開發中有的還會把用戶登陸信息存儲到cookie中。那麼,從安全的角度來考慮的話,你應該但願你的網站的cookie不能被另一個網站使用(否則cookie中的數據就很是容易被別人竊取了),因此這就引入了域的概念,經過域來限制資源的使用,攔截跨域的資源請求。編程
有不少手段來解決跨域,但常見的用於解決跨域調用接口的問題就是CORSjson
CORS使得瀏覽器在向目的域發起請求以前先發起一個OPTIONS方式的請求到目的域獲取目的域的信息,好比獲取目的域容許什麼域來請求的信息。
此時目的域一般須要在響應頭中添加如下信息:
但有時候發請求是不會觸發OPTIONS請求的。若是這個請求符合如下條件的話:
1.請求的方式是GET、POST或HEAD。
2.請求頭屬於Accept,Accept-Language,Content-Language,Content-Type ,Viewport-Width。
3.請求頭中Content-Type屬於application/x-www-form-urlencoded、multipart/form-data、text/plain中的一個。這種請求也被稱爲「簡單請求」。
下面的例子能夠用於測試簡單和非簡單請求是否會發OPTIONS請求
<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>用於測試簡單請求</title> </head> <body> <button onclick="senddata()">XMLHttpRequest請求</button> <script type="text/javascript"> var xmlhttp=new XMLHttpRequest(); function senddata(){ xmlhttp.open("GET","https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1565884598468&di=bb6ccc7a3280b183e4e13c852bc8353c&imgtype=0&src=http%3A%2F%2Fs04.lmbang.com%2FM00%2FCA%2FDE%2FDpgiA1uPAOKAXmJfAADir1hWa-A750.gif",true); xmlhttp.send(); //若是你有一個能夠用來測試的接口,能夠嘗試把這一段註釋了,來測試是否發送OPTIONS請求。 // xmlhttp.open("POST","http://localhost:8080/hello",true); //下面經過加了一個請求頭,使得這個請求不是一個不發OPTIONS的請求。 // xmlhttp.setRequestHeader("Content-Type", "application/json") // xmlhttp.send(); } </script> </body> </html>
下面基於Spring MVC框架來講明後端如何返回返回CORS響應頭(注:在spring mvc中,你能夠直接使用@CrossOrigin
來簡單返回CORS響應頭。)。
前端請求測試代碼:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>用於測試OPTIONS</title> </head> <body> <!-- 跨域資源讀操做,禁止 --> <button onclick="senddata()">XMLHttpRequest請求</button> <script type="text/javascript"> var xmlhttp=new XMLHttpRequest(); function senddata(){ //若是你有一個能夠用來測試的接口,能夠嘗試把這一段註釋了,來測試是否發送OPTIONS請求。 xmlhttp.open("POST","http://localhost:8080/hello",true); //下面經過加了一個請求頭,使得這個請求不是一個不發OPTIONS的請求。 xmlhttp.setRequestHeader("Content-Type", "application/json") xmlhttp.send(); } </script> </body> </html>
當咱們沒有部署接口的時候,咱們就能夠看到,頁面是發了一個OPTIONS請求的:
並且,瀏覽器控制檯也提示如下信息:
當咱們部署了接口的時候,咱們先嚐試不返回正規的CORS響應頭。
因爲沒有返回CORS響應頭,因此OPTIONS沒有請求到合適的CORS信息,因此請求就會被攔截,因此就會報下圖的兩個警告。
當咱們在響應中添加CORS響應頭後,咱們能夠看到咱們剛剛設置的CORS響應頭被OPTIONS請求成功了。
並且請求也被髮出去了:
對於不一樣編程語言的如何使用CORS,能夠自查。