之前在學校作項目的時候,登陸註銷,權限驗證這些事情,都是交給框架來作的,每次都是把這個架子拿到項目中去,也沒有真正思考過它的過程,總覺的這些都是十分簡單的邏輯。前端
然而來公司工做以後,慢慢以爲登陸和權限雖然固定,但確實不容出錯的。而且,在一個大型的公司或是多系統中,登陸和權限都是抽離開來的,它們有複雜的邏輯以及高安全性,而且使得多個系統能夠有一個統一登陸和認證的接口。這就是今天想描述的單點登陸SSO。nginx
1. 關於SSO:ajax
SSO是一種統一認證和受權機制,指訪問同一服務器不一樣應用中的受保護資源的同一用戶,只須要登陸一次,即經過一個應用中的安全驗證後,再訪問其餘應用中的受保護資源時,再也不須要從新登陸驗證。後端
它包含的核心有兩點:瀏覽器
1)全部應用系統共享一個身份認證系統。安全
統一的認證系統是SSO的前提之一。認證系統的主要功能是將用戶的登陸信息和用戶信息庫相比較,對用戶進行登陸認證;認證成功後,認證系統應該生成統一的認證標誌(ticket),返還給用戶。另外,認證系統還應該對ticket進行效驗,判斷其有效性。服務器
2)全部應用系統可以識別和提取ticket信息cookie
要實現SSO的功能,讓用戶只登陸一次,就必須讓應用系統可以識別已經登陸過的用戶。應用系統應該能對ticket進行識別和提取,經過與認證系統的通信,能自動判斷當前用戶是否登陸過,從而完成單點登陸的功能。session
那具體公司的SSO是怎樣的原理和過程呢?架構
首先是我看到的一個原理圖:
其實主要包含兩種狀況:
1. 當在統一認證處沒有認證,或者自己訪問子系統瀏覽器客戶端沒有cookie保存的ticket時,會進行帳號密碼登陸的過程,登陸成功便會經過回調的url返回code給子系統。這時子系統須要拿code到checkCodeUrl去拿ticket和用戶信息。拿到userInfo和ticket後,能夠作一些權限的調用,也能夠對ticket,userInfo信息進行處理加密生成token串(這裏咱們用的是JWT,一會會講到),而後講token放入客戶端的cookie中,以便下次使用。
2. 當訪問子系統的瀏覽器有token信息時,則會經過JWT進行token串的解密,拿到userInfo和ticket,而後攜帶ticket去checkTicketUrl去校驗ticket,而若是統一認證處保存了用戶的登陸信息,則認證經過。
講完這兩種狀況,想貼一下我前兩天看了這部分代碼的畫的一個邏輯草圖:
這裏還有一點在wiki上看到了,想記錄一下:
因爲全部項目都採用先後端分離設計,而先後端分離後,靜態頁面沒法受到後端服務的權限控制。一般做法是頁面請求後發ajax到後端驗證,若是驗證失敗後後端返回跳SSO的必要數據,由前端再302到SSO登陸頁。但這並不符合SSO的接入標準的要求。
如今的解決方式是:
後端加入對根路徑的請求攔截。以如今前端架構設計看,相似於http://domain/#/XXX的請求格式,其實都是對根路徑的請求。
在nginx中加入轉發規則,對根路徑的請求轉到後端服務器上。
後端接收到頁面請求後先經過SSO Check,若是驗證不經過,在後端直接發起重定向到SSO登陸頁。
2. 關於JWT
首先想比較一下傳統的session認證和token的認證方式:
http協議自己是一種無狀態的協議,而這就意味着若是用戶向咱們的應用提供了用戶名和密碼來進行用戶認證,那麼下一次請求時,用戶還要再一次進行用戶認證才行,由於根據http協議,咱們並不能知道是哪一個用戶發出的請求,因此爲了讓咱們的應用能識別是哪一個用戶發出的請求,咱們只能在服務器存儲一份用戶登陸的信息,這份登陸信息會在響應時傳遞給瀏覽器,告訴其保存爲cookie,以便下次請求時發送給咱們的應用,這樣咱們的應用就能識別請求來自哪一個用戶了,這就是傳統的基於session認證。基於session的認證使應用自己很可貴到擴展,隨着不一樣客戶端用戶的增長,獨立的服務器已沒法承載更多的用戶,而這時候基於session認證應用的問題就會暴露出來。
總結一下,session的認證方式,除了增長服務器壓力,還會致使很差擴展,分佈式環境會涉及到session同步的問題,而且還會容易受到CSRF攻擊(這個我尚未細看)。
那麼token的方式就顯得十分輕便和靈活了。客戶端負責存儲token(通常用cookie做爲存儲方式),並在每次請求時附送上這個token值,由服務端完成校驗(就是剛剛SSO的那個流程),這樣一來,也爲擴展提供了遍歷,客戶端不須要知道請求哪一臺服務器。
而後關於JWT的格式,這裏就不細說了,它有三段部分,並用到了Base64加密,使得信息更加安全。經過jwt工具的加密和解析能夠很方便的把須要的信息存入token,知足系統須要的各個需求。
最後再說兩個點吧:
1) 因爲jwt是對稱加密,是能夠被解密的,因此有些極爲敏感的數據仍是不要放在token中爲好。
2) jwt還支持token的過時處理和刷新等功能,但咱們的系統中並無用到,若是從此有這方面的需求,能夠作一些改進吧。