什么是單點登錄
簡單點說就是公司有A,B兩個系統,我登錄了A系統之后再跳轉到B系統可以直接訪問,而不需要再次登錄B系統.
幾種常見的單點登錄實現方式
在講解單點登錄之前先講解幾個基本的概念:
Cookie:
Cookie是一段不超過4KB的小型文本數據,是保存在用戶本地的,常見格式為:
Expires屬性:設置Cookie的生存期
Domain屬性:指定了可以訪問該 Cookie 的 Web 站點或域
比如圖中的Domain:192.168.1.72這就表示只能只有1.72下的請求可以使用這個cookie,百度什么的就不能使用這個cookie
Path屬性:定義了Web站點上可以訪問該Cookie的目錄
圖中的Path是/,這表示這個cookie是根目錄擁有的,只要1.72的請求都會默認帶上這個cookie,加入Path是/webaikn,那么只有http://192.168.1.72/webaikn/**的請求會帶上這個cookie,而http://192.168.1.72/webadmin/**就無法使用這個cookie
其他:略
Session:
http請求是無狀態的,但是我們日常訪問系統的時候都是希望系統能記住我這個用戶,這時候就要靠session去實現,因此session成為會話控制.但是光靠session還是無法實現會話控制的,還需要cookie的配置,如圖所示:
這個JESSIONID就是保持會話的關鍵,它的value對應的就是該用戶在服務器的sessionId,所以我們代碼直接寫HttpSession session = request.getSession(); 才不會數據錯亂.
Ps:session的存在方便了我們的開發,但是也在一定程度上增加了麻煩,比如多機部署時候的seesion丟失,
重定向
一句話,轉發是服務器行為,重定向是客戶端行為.
轉發和重定向都可以由java后台實現,例如:
請求轉發:
request.getRequestDispatcher("/user").forward(request,response);
重定向:
response.sendRedirect(request.getContextPath + "/user")
當設置轉發之后,請求會直接去轉發的地址,而重定向的話請求會先返回客戶端,然后再由客戶端重新發起請求去新的地址.這里就隱藏了一個知識點,當我在后台設置了cookie然后重定向的時候,其實我重定向的請求中已經帶上了我設置的cookie
(1) 假設A和B兩個系統都部署在192.168.110.110服務器上
用戶在登錄了A系統之后,后台代碼設置將userName和password作為cookie存入到用戶的瀏覽器中並將cookie的domain設置為192.168.110.110,path設置為/
之后訪問B系統的時候由於大家的Ip都是一樣的,所以B系統能夠獲取到A系統設置的cookie,這是只需要設置一個攔截器,在攔截器中判斷用戶是否是登錄狀態,如果未登錄就去request中獲取cookie信息,獲取到之后解密然后模擬登錄,這樣用戶可以無感知的登錄到B系統.
點評:這是典型的同域單點登錄實現方式,局限性非常大,必須要兩個系統在同一個服務器或者二級域名相同的情況下才能實現,一般稱為偽單點登錄
(2) 知識庫系統的單點登錄實現
知識庫的方案1的基礎上增加了Nginx作為反向代理(有反向代理就有正向代理,自行查找資料什么是正向代理什么是反向代理)
雖然webaikn和webadmin部署在不同的服務器,但是對客戶是無感知的,由於都是訪問Nginx,然后再由nginx做轉發代理,所以域名是同一個,這樣cookie也是可以共享的,這里有一個點需要注意一下,webaikn可能是多機部署,所以nginx在做轉發的時候需要設置ip_hash策略,目的就是保證用戶上一次請求訪問的哪台服務器,下一次還是訪問那一台服務器,不至於導致session丟失的情況.
點評:解決了多機部署單點登錄失效的情況,但是還是需要服務器端保存用戶的session狀態,一方面對於服務器端會產生內存壓力,另一方面需要配置ip_hash導致流量不均衡,某些服務器壓力比較大的情況.而且用戶名和密碼保存在cookie中也存在一定的安全隱患,只要被截取到一次請求都會造成賬戶被盜的情況
(3) 跨域token實現單點登錄
主要步驟:
- 用戶登錄A系統,A系統攔截器發現請求沒有帶token,於是重定向到單點登錄認證中心sso系統,注意帶上用戶之前請求的url,我們后面就叫oldUrl
- Sso接收到請求,發現request的cookie中沒有登錄成功的令牌token,於是重定向到本系統的登錄頁面,繼續帶着oldUrl
- 用戶輸入用戶名和密碼,提交
- Sso驗證用戶名是否正確,不正確繼續重定向到登錄頁面,如果正確,進行下面的操作:
生成一個cookie,name就叫token,value可以是任意不重復的值,uuid就行(注意這個cookie是瀏覽器和sso系統之間的)
將用戶信息保存到redis中,key是生成的uuid,value就是user對象
重定向到oldUrl的地址,注意要拼接上token參數
- A系統再次收到請求,不同的是這次有token參數,A系統根據token的值去redis驗證,這里需要分情況討論了
沒有找到:說明其他子系統發起了注銷操作,需要重定向到sso登錄頁面
找到了:有了User對象之后可以判斷當前請求是否在用戶權限表中,存在就直接放行,不存在返回權限不足,之后的請求都需要將token放到請求頭信息或者url中
- 用戶瀏覽完A系統之后,准備去B系統轉轉,於是瀏覽器向B系統發起請求,B系統攔截器收到請求,發現請求沒有帶token,發起重定向去sso,記得帶上本次請求的oldUrl
- 這時候其實和上面的第二步差不多,區別在於由於之前登錄過sso所以這次的request中是有token的cookie的,所以sso只需要重定向到oldUrl指向的地址就行,同時記得將cookie中取出來的token拼接到url中
- B再次系統收到請求,之后的操作和步驟5是一樣的了
點評:獨立出單點登錄認證中心,統一做權限認證操作,清晰明了
子系統不需要用session保存用戶登錄狀態,減輕了服務器的負擔
每次請求都是以token作為驗證標准,就算請求被攔截了,用戶的信息也不會泄露
后期做三方登錄的時候也不需要將用戶數據暴露給其他系統,其他系統能獲取的只有token(真要做三方登錄redis中存放的肯定是最簡單的一些用戶信息)
下面這個圖取自哪位大佬我已經沒有地址了,好像是百寶門