登錄是一個網站最基礎的功能。有人說它很簡單,其實不然,登錄邏輯很簡單,但涉及知識點比較多,如:
我們看一下傳統的做法,前後端統一在一個服務中:
如圖所示,邏輯處理和頁面放在一個服務中,用戶輸入用戶名、密碼後,後臺服務在session中設置登錄狀態,和用戶的一些基本信息,
然後將響應(Response)返回到瀏覽器(Browser),並設置Cookie。下次用戶在這個瀏覽器(Browser)中,再次
近幾年,隨著前後端分離的流行,我們的項目結構也發生了變化,如下圖:
我們訪問一個網站時,先去請求靜態服務,拿到頁面後,再非同步去後臺請求數據,最後渲染成我們看到的帶有數據的網站。在這種結構下,
這裡又分兩種情況,服務A和服務B在同一域下,服務A和服務B在不同域下。在詳細介紹之前,我們先普及一下瀏覽器的同源策略。
同源策略是瀏覽器保證安全的基礎,它的含義是指,A網頁設置的 Cookie,B網頁不能打開,除非這兩個網頁同源。
所謂同源是指:
例如:http://www.a.com/login,協議是http,域名是www.a.com,埠是80。只要這3個相同,我們就可以在請求(Request)時帶上Cookie,
http
www.a.com
80
同域下的前後端分離
我們瞭解了瀏覽器的同源策略,接下來就看一看同域下的前後端分離,首先看服務端能不能設置Cookie,具體代碼如下:
後端代碼:
@RequestMapping("setCookie") public String setCookie(HttpServletResponse response){ Cookie cookie = new Cookie("test","same"); cookie.setPath("/"); response.addCookie(cookie); return "success"; }
我們設置Cookie的path為根目錄"/",以便在該域的所有路徑下都能看到這個Cookie。
前端代碼:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>test</title> <script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.8.0.js"></script> <script> $(function () { $.ajax({ url : "/test/setCookie", method: "get", success : function (json) { console.log(json); } }); }) </script> </head> <body> aaa </body> </html>
我們在瀏覽器訪問http://www.a.com:8888/index.html,訪問前先設置hosts,將http://www.a.com指向我們本機。訪問結果如圖所示:
我們可以看到伺服器成功設置了Cookie。然後我們再看看同域下,非同步請求能不能帶上Cookie,代碼如下:
@RequestMapping("getCookie") public String getCookie(HttpServletRequest request,HttpServletResponse response){ Cookie[] cookies = request.getCookies(); if (cookies != null && cookies.length >0) { for (Cookie cookie : cookies) { System.out.println("name:" + cookie.getName() + "-----value:" + cookie.getValue()); } } return "success"; }
前端代碼如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>user</title> <script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.8.0.js"></script> <script> $(function () { $.ajax({ url : "http://www.b.com:8888/test/getCookie", method: "get", success : function (json) { console.log(json); } }); }) </script> </head> <body>
</body> </html>
訪問結果如圖所示:
再看看後臺列印的日誌:
name:test-----value:same
同域下,非同步請求時,Cookie也能帶到服務端。
所以,我們在做前後端分離時,前端和後端部署在同一域下,滿足瀏覽器的同源策略,登錄不需要做特殊的處理。
不同域下的前後端分離
不同域下,我們的響應(Response)能不能設置Cookie呢?請求時能不能帶上Cookie呢?我們實驗結果如下,這裡就不給大家貼代碼了。
由於我們在http://a.com域下的頁面跨域訪問http://b.com的服務,http://b.com的服務不能設置Cookie。
如果http://b.com域下有Cookie,我們在http://a.com域下的頁面跨域訪問http://b.com的服務,能不能把http://b.com的Cookie帶上嗎?答案是也帶不上。那麼我們怎麼解決
JSONP解決跨域
JSONP的原理我們可以在維基百科上查看,上面寫的很清楚,我們不做過多的介紹。我們改造介面,
@RequestMapping("setCookie") public String setCookie(HttpServletResponse response,String callback){ Cookie cookie = new Cookie("test","same"); cookie.setPath("/"); response.addCookie(cookie); if (StringUtils.isNotBlank(callback)){ return callback+"(success)"; } return "success"; }
@RequestMapping("getCookie") public String getCookie(HttpServletRequest request,HttpServletResponse response,String callback){ Cookie[] cookies = request.getCookies(); if (cookies != null && cookies.length >0) { for (Cookie cookie : cookies) { System.out.println("name:" + cookie.getName() + "-----value:" + cookie.getValue()); } } if (StringUtils.isNotBlank(callback)){ return callback+"(success)"; } return "success"; }
如果callback參數不為空,將返回js函數。前端改造如下:
設置Cookie頁面改造如下:
<script> $(function () { $.ajax({ url : "http://www.b.com:8888/test/setCookie?callback=?", method: "get", dataType : jsonp, success : function (json) { console.log(json); } }); }) </script>
請求Cookie時改造如下:
<script> $(function () { $.ajax({ url : "http://www.b.com:8888/test/getCookie?callback=?", method: "get", dataType : jsonp, success : function (json) { console.log(json); } }); }) </script>
所有的請求都加了callback參數,請求的結果如下:
很神奇吧!我們設置了http://b.com域下的Cookie。 如果想知道為什麼?還是看一看JSONP的原理吧。我們再訪問第二個頁面,看看Cookie能不能
好了,不同域下的前後端分離,可以通過JSONP跨域,從而保持登錄狀態。 但是,jsonp本身沒有跨域安全規範,一般都是後端進行安全限制,
CORS解決跨域
CORS是一個W3C標準,全稱是"跨域資源共享"(Cross-origin resource sharing)。CORS需要瀏覽器和伺服器同時支持。目前,所有瀏覽器都支持該功能,IE瀏覽器不能低於IE10。
CORS請求默認不發送Cookie和HTTP認證信息。若要發送Cookie,瀏覽器和服務端都要做設置,咱們要解決的是跨域後的登錄問題,所以要允許跨域發送
後端要設置允許跨域請求的域和允許設置和接受Cookie。
@RequestMapping("setCookie") @CrossOrigin(origins="http://www.a.com:8888",allowCredentials = "true") public String setCookie(HttpServletResponse response){ Cookie cookie = new Cookie("test","same"); cookie.setPath("/"); response.addCookie(cookie); return "success"; }
@RequestMapping("getCookie") @CrossOrigin(origins="http://www.a.com:8888",allowCredentials = "true") public String getCookie(HttpServletRequest request,HttpServletResponse response){ Cookie[] cookies = request.getCookies(); if (cookies != null && cookies.length >0) { for (Cookie cookie : cookies) { System.out.println("name:" + cookie.getName() + "-----value:" + cookie.getValue()); } } return "success"; }
我們通過@CrossOrigin註解允許跨域,origins設置了允許跨域請求的域,allowCredentials允許設置和接受Cookie。
@CrossOrigin
origins
allowCredentials
前端要設置允許發送和接受Cookie。
<script> $(function () { $.ajax({ url : "http://www.b.com:8888/test/setCookie", method: "get", success : function (json) { console.log(json); }, xhrFields: { withCredentials: true } }); }) </script>
<script> $(function () { $.ajax({ url : "http://www.b.com:8888/test/getCookie", method: "get", success : function (json) { console.log(json); }, xhrFields: { withCredentials: true } }); }) </script>
我們訪問頁面看一下效果。
沒有Cookie嗎?別急,我們再從瀏覽器的設置裏看一下。
有Cookie了,我們再看看訪問能不能帶上Cookie,後臺列印結果如下:
我們使用CORS,也解決了跨域。
前後端分離,基於Cookie-Session機制的登錄總結如下
作者:羅那爾少
鏈接:http://www.imooc.com/article/288333
來源:慕課網
本文原創發佈於慕課網 ,轉載請註明出處,謝謝合作
推薦閱讀: