作用
在一個系統登錄后,其他系統也能共享該登錄狀態,無需重新登錄。
演進
cookie → session → token →單點登錄
Cookie
可以實現瀏覽器和服務器狀態的記錄,但Cookie會出現存儲體積過大和可以在前后端修改的問題
Session
為了解決Cookie的數據敏感問題,Session應運而生。
- 常見的實現方式是基于Cookie。只將口令放置Cookie,通過口令實現前后端數據的映射,大部分數據存儲于服務器的session中,這里會有一個全局sessions去存儲每個用戶的session,并設置每個的有效期,一般20分鐘。超時則重新生成和更新全局sessions。
- 分布式問題:通常服務器是集群,用戶的請求會走到負載均衡,不一定打在同一臺登錄請求的機器上,那不就 session 失效了嗎
- 解決方案:
- 第一種,把 session 集中存儲,給個獨立的 redis 或普通數據庫
- 第二種,將相同 ip 請求的負載均衡都打在同一臺機器上。
- 通常是第一種,因為第二種的同一臺機器會出現請求過多宕機的問題。
- 解決方案:
token
-
解決場景
session 的維護給服務端造成很大困擾,我們必須找地方存放它,又要考慮分布式的問題,甚至要單獨為了它啟用一套 Redis 集群。有沒有更好的辦法?——token
登錄場景不需要往 session 存太多東西,那為何不直接打包到 cookie 中呢?這樣服務端就不用存了,每次核驗 cookie 帶的 token 有效性就可以了,還可以存一些輕量的信息。
-
HTTP 頭部
token 認證則不需要后臺保存,token一般放在HTTP請求頭的 Authorization 中
-
流程
- 輸入用戶密碼,通過的話,服務器返回 token
- 瀏覽器每次攜帶該 token 去請求數據
來源
最早起源于多服務器間 session 數據一致的問題。
方案
關鍵如何讓 Session ID(或 Token 在多個域中共享)
不同的項目情況,適用不同的方案,適合的才是最好的
按照域名劃分方式分類:
同主域
一個企業,一般情況下只有一個域名,通過二級域名區分不同的系統。
二級域名不同也算跨域。當協議,域名,端口其中一個不同時,就算跨域了。
例如 aliyun.com
,其他業務系統分別為:a.aliyun.com
、b.aliyun.com
,要做單點登錄,需要一個登錄系統,叫做 account.aliyun.com
,我們只要在 acount.aliyun.com
登錄了,a.aliyun.com
和 b.aliyun.com
也登陸了
實現:
- 方法一: Root Cookie 方式
-
原理:同一域名下的不同子域名屬于同一主域名,主域名下的 cookie 在不同子域名中都是可見的。
-
將得到的 ticket 寫入根 Cookie 里,通過修改 domain。這一步前后端擇一寫即可
利用二級域名寫一級域名的 Cookie,
acount.aliyun.com
登錄后,可將 cookie 的域設置為頂域,即.aliyun.com
,這樣所有的子域都可以訪問到頂域的 cookie-
前端:react 和 vue 都有 js-cookie 包
import Cookies from 'js-cookie' Cookies.set('key', 'value', { domain: 'localhost' })
前提:后端沒有配置 httpOnly,否則無法通過 document.cookie 方式讀寫,但有些瀏覽器仍支持寫入。
后端:java
Cookie cookie = new Cookie("key", "value");cookie.setDomain('.example.com')
無需在 domain 后加上端口號。設置完后,同主域名的網址打開瀏覽器的存儲,便能看到 cookie 了。
-
-
總結:實現簡單,不支持跨主域名的方式
-
- 方法二:postMessage + iframe 方案,無需后端參與
-
可以實現同主域和不同主域,不同的是同主域的實現即便關閉后,再打開也沒問題。不同主域的,關閉后就不會保存了。監聽 message 的同時,在 postMessage 后,窗口就會觸發了。
-
cross-storage 庫
,實現了對該方案的封裝- 原理:類似中轉站,所有的登錄信息的存儲或獲取都通過該中轉站
- 使用注意
-
如果無法連接成功的,可以考慮將初始化代碼放入 html 中,并單獨開啟一個服務器(例如小型 node),用來對該頁面的訪問。
-
CrossStorageHub.init 時,localhost 和 域名都要寫上
CrossStorageHub.init([{origin: /.*localhost:808\d$/, allow: ['get', 'set', 'del']},{origin: /.*data.com:300\d$/, allow: ['get', 'set', 'del']} }]
-
-
缺陷:Safari 瀏覽器不兼容該方案
-
原因:postMessage + iframe(cross-storage)由于 safari 瀏覽器只支持和 iframe 通信,不支持新窗口通信,無論是通過 iframe.src 帶上 token 的 query 還是通過 postMessage 存儲到 localStorage 里。新打開一個窗口后,localStorage 里都沒有之前的存儲值。
-
cross-storage 的文檔,也給出了降級方案
- 可以采用 root cookie 方式
- 讓服務端返回的方式
- 直接去改變safari瀏覽器的配置。 (不建議)
- 跳轉的 url 鏈接帶上該 token 的 query,前提是要點擊后才能觸發。再保存到本地
-
-
不同主域
實現:
-
方法一:
部署一個 SSO 認證中心,專門負責處理登錄請求(登錄、登出、獲取用戶信息、當前用戶狀態),是單點登錄的標準做法。
類似中轉站,所有子系統的登錄都要詢問一遍這個中轉站
CAS: 基于 SSO 認證中心的開源項目代表,Central Authentication Service 即中央認證服務
-
方法二:iframe + postMessage 的方式
同上