今天跟大家聊一個比較基礎的話題,就是實現登入的方式有哪些?適合剛入行的朋友。
華山之 Session 絕學
Session 我們稱之為會話控制, 是一種在伺服器端保持會話狀態的解決方案。通俗點來講就是客戶端訪問服務端的時候,會在服務端儲存對應的資訊,生成一個 Session ID 返回給客戶端,客戶端下次過來的時候帶上 Session ID,這樣就能識別訪問者的身份。
請求中帶上 Session ID 最常見的方式就是通過 Cookie 來承載了,Cookie 是客戶端儲存使用者資訊的一種機制,在瀏覽器環境中,請求會自動帶上 Cookie 資訊,服務端也就能獲取到 Session ID。
在後端實現登入邏輯的時候,先獲取 HttpSession 物件,然後通過 setAttribute()來設定登入的使用者資訊,比如使用者 ID。驗證有沒有登入的時候通過 getAttribute()來獲取對應的 Session 資訊,如果沒有獲取到,則證明沒有登入過或者會話失效了。
對於 Tomcat, Jetty 這些容器而言,Session 就是是一塊在伺服器開闢的記憶體空間,儲存結構就是 Map。
Tomcat 的 Session 實現類是 StandardSession。
分散式 Session 解決方案
如果你的應用是單節點部署,這種場景使用web容器實現的 Session 機制沒有問題。一但壓力過大,需要多節點部署的時候,Session 就需要進行分散式的支援。
看下圖,當部署了兩個 Tomcat 的時候,通過 Nginx 進行負載均衡,第一次請求轉發到了 Tomcat1, Session 資訊儲存在 Tomcat1 上面。第二次請求轉發到了 Tomcat2 上面,但是 Tomcat2 上面是沒有剛才的 Session 資訊,這就是多節點下 Session 會出現的問題。
Session 複製
Tomcat 內建了 Session 複製的功能,也就是你的 Session 是在 Tomcat1 中產生的,Tomcat1 會將你的 Session 同步給 Tomcat2, 這樣當你的請求到了 Tomcat2 的時候,就能知道你的身份資訊。
這種方案在其他的框架中也經常能見到,比如 Spring Cloud 體系中的 Eureka 註冊中心,也是採用複製的方式來同步登錄檔的資訊。
關於 Tomcat Session 複製相關配置請參考官方文件:https://tomcat.apache.org/tomcat-8.0-doc/cluster-howto.html
黏性會話
黏性會話指的是對於同一個使用者的請求,永遠都只轉發到某一個 Tocmat 的例項上,這樣即使沒有做 Session 複製,也不會出現問題。如果有節點掛掉了就會訪問失敗。
常見的方式有對 IP 做 Hash 進行轉發,IP 不太可靠,因為會變。在 Nginx 中有一個 nginx-sticky-module 這個第三方模組用於新增一個粘性 Cookie,該粘性 cookie 始終轉發到同一伺服器。
nginx-sticky-module 會在 Cookie 中記錄一個值來標識當前請求需要被轉發到哪個節點,第一次沒有的時候會先轉發,然後在響應給客戶端之前寫入 Cookie。後面的請求都會在 Cookie 找到對應的標識,然後進行轉發到固定的節點。
Session 集中儲存
Session 複製會佔用伺服器資源,影響效能。黏性會話存在單點故障風險。更好的分散式 Session 方式就是集中式儲存。
所謂集中式儲存就是將會話資訊統一儲存在某個地方,像 Tomcat 之類的 Web 伺服器本身不儲存會話資訊,這樣後端服務也就是無狀態的,方便隨時擴容。
至於實現方案的話有很多,大家可以自己去實現 HttpSession 做對應的儲存讀取邏輯,也可以採用開源的方案。比如 Spring Session 就是一個很好的開源方案,上手簡單,支援多種儲存方式,比如 Redis, Mysql 等。
如果對手寫 Spring Session 原理感興趣的,也可以參考我之前的這套課程:http://cxytiandi.com/course/5
少林之 Token 絕學
Token 認證是目前主流的認證方式之一,Token 最大的優勢在於無狀態,並且不用儲存會話資訊。也就是說通過 Token 就可以知道當前訪問的使用者是誰,不需要去 Web 容器的記憶體中獲取,不需要去集中管理會話的儲存中去獲取。
Token 的生成方式有多種,可以自己定義固定的格式,比如裡面包含了使用者 ID,使用者名稱等資訊。也可以使用目前主流的 JWT 方式。
JWT(JSON Web Token)是為了在網路應用環境中傳遞宣告而執行的一種基於 JSON 的開放標準。JWT 的宣告一般被用在身份提供者和服務提供者間傳遞被認證的使用者身份資訊, 以便從資源伺服器獲取資源。
比如在使用者登入時,基本思路就是使用者提供使用者名稱和密碼給認證伺服器,伺服器驗證使用者提交資訊的合法性;如果驗證成功,會產生並返回一個 Token,後續請求使用者帶上這個 Token ,服務端就可以識別這個請求的身份資訊。
JWT 由三部分構成,
- 第一部分是頭部(Header);
- 第二部分是訊息體(Payload);
- 第三部分是簽名(Signature)。
一個 JWT 生成的 Token 格式為:
token = encodeBase64(header) + '.' + encodeBase64(payload) + '.' + encodeBase64(signature)
頭部的資訊通常由兩部分內容組成,令牌的型別和使用的簽名演算法,比如下面的程式碼:
{ "alg": "HS256", "typ": "JWT" }
訊息體中可以攜帶一些應用需要的資訊,比如使用者 ID,程式碼如下:
{ "id": "1001", "name": "yinjihuan"}
簽名是用來判斷訊息在傳遞的路徑上是否被篡改的,從而保證資料的安全性,格式如下:
HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
通過這三部分就組成了我們的 JSON Web Token。
如何使用請參考 Github:https://github.com/jwtk/jjwt
如上圖所示:請求到達 Tomcat 後,可以呼叫單獨的 Token 服務進行 Token 的生成,也可以將 Token 的生成邏輯封裝成一個 jar 包來使用。需要注意的是如果用內嵌的方式,對應 Token 的加密配置要一致,否則會出現驗證失敗的情況。
Token 有點不好的地方在於無法主動讓它失效,比如我們用 Session 的場景,使用者退出登入,直接將 Session 資訊在服務端刪除即可,即使後面用相同的 Session 資訊去請求,服務端也找不到對應的資訊了。
Token 是一個加密的字串,裡面包含了使用者的資訊,加密演算法,過期時間。如果過期時間設定的比較長,也就意味著在過期時間之前都可以使用。
如果要實現退出登入的功能,既然不能對 Token 本身的過期時間進行改造,那麼可以使用一個黑名單的機制來進行過濾即可。將退出登入的 Token 儲存起來,使用的地方去匹配是否登出了,然後進行攔截即可。
關於作者:尹吉歡,簡單的技術愛好者,《Spring Cloud 微服務-全棧技術與案例解析》, 《Spring Cloud 微服務 入門 實戰與進階》作者, 公眾號猿天地發起人。
我整理了一份很全的學習資料,感興趣的可以微信搜尋「猿天地」,回覆關鍵字 「學習資料」獲取我整理好了的 Spring Cloud,Spring Cloud Alibaba,Sharding-JDBC 分庫分表,任務排程框架 XXL-JOB,MongoDB,爬蟲等相關資料。