[OIDC in Action] 2. 基於OIDC(OpenID Connect)的SSO(純JS客戶端)


在上一篇基於OIDC的SSO的中涉及到了4個Web站點:

  1. oidc-server.dev:利用oidc實現的統一認證和授權中心,SSO站點。
  2. oidc-client-hybrid.dev:oidc的一個客戶端,采用hybrid模式。
  3. oidc-client-implicit.dev:odic的另一個客戶端,采用implicit模式。
  4. oidc-client-js.dev:oidc的又一個客戶端,采用implicit模式,純靜態網站,只有js和html,無服務端代碼。

其中hybrid和implicit這兩個站點都是具有在服務端執行代碼的能力的(1,登錄需要在服務端做跳轉;2,登錄狀態寫入cookie;3,通過服務端的接口接收被動的退出通知)。而js這個客戶端則是一個純粹的靜態網站,那么它是如何處理登錄和退出的呢?

oidc-client-js.dev這個web站點對應的代碼位於web.oidc.client.js這個文件夾中(https://github.com/linianhui/oidc.example/tree/master/src/web.oidc.client.js):

JS Client 登錄

我們知道在瀏覽器中的JS是可以直接進行頁面跳轉的,oidc的js客戶端就是利用這個來直接構造認證請求的URL,然后進行登錄跳轉的(我們這里使用的是oidc-client.js這個開源的js庫來處理oidc規范相關的一下操作的)。下圖是打開oidc-client-js.dev后的頁面:

JS Client 直接發起認證請求

我們點擊下Login。

可以看到Client這邊在對oidc-server.dev這個站點發起了2個請求之后就直接構造了一個認證請求的URL,並交給瀏覽器去發起了請求。

  1. /account/js:我自己擴展的,后面介紹其用途,目前暫時忽略它。
  2. /.well-known/openid-configuration:這個是之前介紹到的OIDC提供的Discovery服務,Client需要從這個服務返回的JSON中獲取認證請求的接口地址以及其他的信息。

認證請求這里面包含的參數和上篇中的信息並沒有什么差別,這里就不介紹了。

OIDC-Server 通過URL的#把數據傳遞給JS Client

瀏覽器會重定向到登錄頁面,我們登錄一下,登錄成功后會跳轉上面所填寫的redriect_uri參數指定的URL,並使用URL的#部分攜帶認證后的信息:

http://oidc-client-js.dev/oidc/login-callback.html#id_token=eyJhbGciOiJSUzI1NiIsImtpZCI6ImI2MmE2YTVlZjNiNGJmOTlhNWU3M2FkZmI1OTQ3NjRjIiwidHlwIjoiSldUIn0.eyJuYmYiOjE1MTE3NTEyMjgsImV4cCI6MTUxMTc1MTUyOCwiaXNzIjoiaHR0cDovL29pZGMtc2VydmVyLmRldiIsImF1ZCI6ImpzLWNsaWVudCIsIm5vbmNlIjoiZGU5NGI1NGMzNDk1NGFjMjg0Y2I0NzlhY2M5ZGMxMjMiLCJpYXQiOjE1MTE3NTEyMjgsImF0X2hhc2giOiJ1QnJhckVLOHk4elhwR0pJOG1BaE1nIiwic2lkIjoiMWQ2ZWQwYWI0ZjdhNzI2MDUxMzBiYjBkYjNiOTdkY2MiLCJzdWIiOiIwMDAwMDEiLCJhdXRoX3RpbWUiOjE1MTE3NTEyMjgsImlkcCI6ImxvY2FsIiwiYW1yIjpbImV4dGVybmFsIl19.EUMT0R34OKDuE8AESEnRAASoRCP2NCAy7EEkMdM9vBwfz8BGnrCGXiDnKoUgbw3qK8ekoiwhSed6qE-Xh5QqnnwQTOc_D0nucbA3CVqKDhc9TFonEHoU60ETbX0i70bbOThZeoJdto9CkILbcewk2SLgfCQXZzsKERm6AS7m9LUN7cGjQJQm6Ht5DpIgjFu7s9V7qnUfu7hjvI51zPmYgJwLtvCXb9vAxXy17oBrVTmYunDLETRnfj9UXcsSROOW6Ac6sKSLOtFkY5ElZuIa5Za_1GJFDaoYyZwFT53WWBO9-LBdIHd8Cqx5fyw8tlpT3qmdwf0scSr256sVXykGQw&access_token=eyJhbGciOiJSUzI1NiIsImtpZCI6ImI2MmE2YTVlZjNiNGJmOTlhNWU3M2FkZmI1OTQ3NjRjIiwidHlwIjoiSldUIn0.eyJuYmYiOjE1MTE3NTEyMjgsImV4cCI6MTUxMTc1NDgyOCwiaXNzIjoiaHR0cDovL29pZGMtc2VydmVyLmRldiIsImF1ZCI6WyJodHRwOi8vb2lkYy1zZXJ2ZXIuZGV2L3Jlc291cmNlcyIsIm15LWFwaSJdLCJjbGllbnRfaWQiOiJqcy1jbGllbnQiLCJzdWIiOiIwMDAwMDEiLCJhdXRoX3RpbWUiOjE1MTE3NTEyMjgsImlkcCI6ImxvY2FsIiwic2NvcGUiOlsib3BlbmlkIiwicHJvZmlsZSIsIm15LWFwaSJdLCJhbXIiOlsiZXh0ZXJuYWwiXX0.BU65olTuhLSlyFHyRzSHKUaFw5v5qMg7qmutl3LCel0gtjD9ky9cyD3rUNNAkalVcHXg7znN_F2JB71ape9mSD_L66H8pTTwaiMTxbPz9_HMEz9w6GgmOrjMGP8unIpCOKom1DV4EiSQoDe8P30oh2mwmA5SLvZixlAln3_ycArTd7440SCUrUvnEa1CJyl-K-kkLvLyl3TRo6bDE-H47-AzHq1NtA22cwleVXNxUtOsMHk1Nsa2tOFW6B4t3fAvo_MWx2BFkJMBToy4ArepLXSaN5CQQxH8na1Havll3Ly3c9GOslNsm1krMvx9GYdFR6DgjoDvNbaVDkLdmO2T_w&token_type=Bearer&expires_in=3600&scope=openid%20profile%20my-api&state=96af404863044d49b6e14a5827862538&session_state=C33U-BpeYNeLhWhUuKLfup18cjKkKX54yCdL2fUOtV0.5ec877a6108fde6ad04e774a770d7ee1

這里相比上一篇中返回的信息多了一個access_token是因為我們的認證請求的response_type設置為了“id_token token”,故而oidc-server.dev把id_token和access_token一並給到了客戶端。

JS Client 解析#中的數據,保存自己的登錄狀態

解析#后面的數據也是通過oidc-client.js 這個開源的庫來實現的。解析后的數據呈現在頁面上是如下這個樣子。

其中登錄后用戶的信息保存在瀏覽器的SessionStorage中:

JS Client 主動登出

退出操作和其他類型的客戶端一致,也是先清理自己保存的Session Storage,然后通知oidc-server.dev進行登出,這里就不詳細解釋了。

JS Client 被動登出

我們知道在SSO中,除了自身主動退出登錄之外,還有其他的Client退出的時候,這里的JS Client也要被動的登出。由於JS Client沒有服務端在服務端執行代碼的能力,其登錄狀態也是保存在客戶端這邊的,那么它就沒辦法接收像其他的客戶端一樣接收到登出的通知了。這個時候就需要客戶端自己主動去oidc-server.dev檢查登錄狀態了。這一部分在OIDC中也有標准規范,體現在OIDC的Discovery服務中的check_session_iframe字段中。

這個地址checksession的地址是oidc-server.dev的地址,那么這個地址返回的html頁面中,就可以通過js來檢查去存儲在cookie中的session信息的是否發生變化。然后通過H5中新增的postMessage來把這種變化傳遞給js client這邊。js client再去檢查一下是否已經登出了。如果已經登出,則會清理自身的登錄狀態來完成被動的登出操作。

比如下圖。我再oidc-client-implicit.dev點擊登出的時候,會觸發oidc-server.dev清理自己的cookie,然后js-client中使用check_session_iframe這個隱藏的iframe可以檢測到這種變化,從而使得js-client可以得知用戶已經再其他的client退出登錄。

自動登錄

前面提到JS Client會加載一個oidc-server.dev/account/js的JS腳本文件,這個是我自己擴展出來的一個腳本。它會在這個純靜態的網站在一開使打開的時候告訴客戶端oidc-server.dev是否已經登錄了。

然后靜態的網站就可以利用account這個變量來決定是否再打開網站的時候就自動去登錄(由於其他站點已經登陸過了,那么oidc-server.dev站點會自動攜帶登錄后的信息的再次跳轉回來)。

讀者可以打開瀏覽器,先打開oidc-client-implicit.dev這個站點並且登錄,然后再打開oidc-client-js.dev這個站點的時候,就會發現它會自動的登錄成功了。

總結

 本篇介紹了再瀏覽器中運行的純靜態的HTML網站使如何使用OIDC服務進行單點登錄,統一登出,登錄狀態監控,以及附加的如何讓JS Client自動登錄的原理。這里所使用的例子使傳統的HTML+JS的結構。如果你使采用的Vue,Angular或者React的這類前端框架的話,那么其本質上的原理也是完全一樣的,因為不管使采用的什么框架,最終輸出給瀏覽器的還是HTML+JS而已。

參考

本文源代碼:https://github.com/linianhui/oidc.example

oidc-client.js:https://github.com/IdentityModel/oidc-client-js


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM