1. 程式人生 > >網頁登入以及單點登入的一些概念

網頁登入以及單點登入的一些概念

開發web應用,不免要涉及到登入以及許可權等問題,獨立的站點裡,這些也不存在太大的問題,反正教程、程式碼很多的,但是在照抄程式碼的時候,應該還要考慮一下別人為什麼要這樣寫,以後才能自己寫,才能根據不同的需求設計不同的系統。而已經討論的非常多的單點登入,相對就要複雜多了,看了好多次,看的時候好像看懂了,過後一想好像什麼都不知道,具體設計的時候,必須要實現哪些物件,每個物件是用來幹什麼的,又是一頭卻霧水。最近又重新在網上看了一下單點登入的原理,目前的體會是:基礎很重要,也就是說,要想掌握多站點的單點登入,先得熟悉單個站點的登入。

先來回顧一下,單個站點登入及使用者許可權的基本過程。大概畫了個圖,其中涉及到幾個概念需要澄清一下,網上都說,使用者登入成功後,登入資訊儲存在會話裡?會話在哪裡?網頁程式設計,經常用到request、response、Server、Application、Session以及cookise這些物件以及生存週期,前面幾個從名字上來說,意義還是比較清楚的,就是session的概念有些模糊,百度百科上是這樣說的:Session 物件儲存特定使用者會話所需的屬性及配置資訊。這樣,當用戶在應用程式的 Web 頁之間跳轉時,儲存在 Session 物件中的變數將不會丟失,而是在整個使用者會話中一直存在下去。更懵的是,百度百科下面還有一個定義:在計算機專業術語中,Session是指一個終端使用者與互動系統進行通訊的時間間隔,通常指從註冊進入系統到登出退出系統之間所經過的時間。以及如果需要的話,可能還有一定的操作空間。其實很多書上描述的也差不多,然後接下來就是session有哪些方法可用,如何用,然後就是上程式碼……,的確,概念清不清楚不要緊,反正照葫蘆畫瓢,程式照樣能執行,功能照樣實現。存在的問題是,一個概念理解不清,後面的概念就更難理解,甚至會理解錯,越往後就越難,就越不清楚了。比如說,我就一直以為session是在瀏覽器和伺服器之間來回傳送的尷尬

,因為可以從httpservletrequest裡面getsession啊,而request不是客戶端傳送來的請求麼。而我又經常把下拉選擇框、列表框的內容儲存在seession裡面,然後經常想,如果網站複雜了,列表框多了,這些東西傳來傳去多浪費流量啊驚恐。好吧,浪費流量那是使用者的事,但是現在討論的登入問題,使用者名稱、密碼是不是也是在瀏覽器和客戶端之間傳來傳去的呢?顯然不可能啊,太不安全了。所以,決不是這樣的,session只會儲存在伺服器端。反過來,如果session會傳到客戶端,那麼javascript可以訪問裡面的內容嘍,但是從來沒有哪個javascript指令碼教程這樣用過啊。從這個角度,同樣也說明了我以前的理解是錯誤的,好了,這下可以放心地往session裡面放東西了,最多是多佔點伺服器記憶體而已大笑
。但是,別急,但是如果session不傳給客戶端,那麼客戶端如何知道自己是處於哪一個session中,或者說伺服器如何判斷哪一個客戶端是哪一個會話的?一定還是有些什麼在客戶端和伺服器端傳來傳去的吧,肯定是有的,那就是session_id,不同的伺服器,變數名不一樣,但是道理都一樣,這個就是個key或者說一個記憶體指標,反正伺服器根據這個ID,就可以在伺服器記憶體當中找到對應的Session物件來,也就是說API裡面Httpservletrequest.getsession實際上就是根據客戶端請求裡面的sessionid查詢到伺服器端的session,然後就可以訪問裡面的內容了,並不是從request裡面取出session的。瀏覽器訪問伺服器的時候,如果有這個ID就會把它跟隨request一起發給伺服器,這個變數多半是儲存在記憶體或cookies裡,cookies是存在客戶端並且每次都發給伺服器是肯定的,不管哪裡吧,反正這事瀏覽器負責幹,只要找得到,它都會發給伺服器。然後伺服器也並不是就認客戶端發來的這個,最起碼,客戶端發來的ID它要能根據這個ID找得到對應的物件啊。找不到它就會重新申請一段記憶體,然後再把ID發給客戶端,而客戶端呢,就會記著這個新的ID,下次就用這個ID了。關於session大概就是這麼回事了吧,已經偏離主題太遠了。

接著講使用者的登入資訊,使用者的資訊除了使用者名稱、密碼之外,一般還有一個角色,甚至許可權列表,同樣,這些資訊如果在客戶端和伺服器之間傳來傳去的話,同樣是一種資源浪費,並且,不安全。實際上,傳來傳去的只是一個token,token的作用其實和sessionID是一樣的,跟使用者名稱、密碼沒什麼關係,也不需要有關係,就是一個key或者一個指標反正伺服器端能根據這個token找出儲存在session裡面的使用者資訊來,這就夠了。另外,剛才說的Session是由容器管理的,而token通常是由應用程式管理的,也就是說前面圖中關於session的id以及建立session的過程,一般開發人員根本不用去考慮,只需要知道有這麼個東西可以用就行了。這也就是為什麼網上也好,書上也好,大都沒講得很透的原因吧。token就是用應用程式管理的,只不過,不管什麼平臺、什麼框架,使用者身份驗證過程、發放token等都是包裝好了的,只要按平臺框架的要求寫程式碼,能用是沒問題的。但是要理解單點登入,這個底層的概念必須要理解清除了,才好理解上層為什麼這麼做。

很明顯,要實現單點登入,使用者名稱密碼肯定是統一的,但是其他,比如許可權,肯定各個系統還是各個系統的,各個系統肯定還是要按各自的辦法儲存使用者的資訊,包括會話裡面已經登入的使用者的資訊,這個是沒辦法統一的,單點登入涉及到的各個軟體,可能是不同的框架、語言、使用不同的容器和不同的作業系統,也完全沒有可能全部統一。所以,做單點登入,基本上不會、也不可能改變原有系統或者要求所有子系統都要用同一種作業系統、應用伺服器、程式語言、開發框架的,當然,最基本的要求或者對原有系統輕微的改造也是必須的。

好了,繼續講單點登入,其實對於單點登入中的各個子站點來說,實際上只有一點點不同,就是以前是自己用頁面接收使用者名稱密碼自己驗證,現在是交給一個統一的認證中心去認證,只要認證中心認證通過了,照樣在session中儲存使用者資訊,照樣發token,這樣session中的資訊是自己從資料庫裡面取出來的,token是自己發的,以後的事情,還照樣,根據token查詢使用者許可權,根據使用者許可權不同發給客戶端不同的東西,沒什麼變化。很簡單,現在的問題在於其他站點,使用者如果又要訪問其他站點怎麼辦,還是這樣要求使用者發來使用者名稱、密碼,然後交給認證中心去認證肯定不行,那就不叫單點登入了,仍然和各登各的差不多,只不過是使用者名稱、密碼相同的罷了。並且,已經登入的系統也不可能把token發給其他子系統,就連認證中心也不能這樣,一個是不知道使用者什麼時候用什麼系統,使用者沒有訪問其他系統的時候,那些系統裡面連session都沒有,使用者資訊儲存在哪兒,token和什麼對應,再說前面也說了,token就相當於一個記憶體指標,離開伺服器就是一串二進位制數,另外,即便有用,那所有系統之間要互相通迅,還要考慮系統之間信任的問題,得多麻煩啊。還有,退出又怎麼辦?所以問題還得靠認證中心解決。其實搞清楚單點登入的的原理後,也不算複雜,就是要利用使用者已經在認證中心登入,利用使用者已經和認證中心建立了會話,並且已經有認證中心的token,至於這個系統那個系統還有認證中心,使用者的請求被後臺重定向從這裡跳轉那裡,又從那裡跳轉到這裡,這背後的事情,客戶不用參與也感覺不到的。好了,上圖:

這圖有點大哈,抄別人的,但又不是完全照搬,想盡量表達清楚些,就畫得大了些。單點登的過程大概是下面這樣的:

1.使用者訪問server1上的受限制的資源,結果server1發現該使用者沒有token也就是沒有登入,普通的站點呢這時候一般是直接重定向或跳轉到登入頁,單點登入系統的話,就是重定向到sso認證中心去(圖裡面用redirect好像剛好用錯了,反正上圖裡面redirect就是重定向,也就是響應一個地址給瀏覽器,讓瀏覽器去訪問該地址);

2.然後瀏覽器就去訪問sso認證中心,結果sso發現該使用者沒有全域性token也就是沒有在中心登入過,這時候sso就直接響應登入頁了(因為不可能再委託別人了);

3.使用者輸入使用者名稱密碼在sso中心登入,如果登入成功,sso自然是要發一個token給他的,這叫全域性token,表示這個使用者可以正常訪問sso了,然後再生成一個臨時的token給他,並且,叫他用這個token去訪問剛才想訪問的server1,這個地址是剛才使用者第一次訪問sso時帶過來的,sso儲存在session裡的;

4.使用者拿著這個臨時的token去訪問server1,server1一看拿著個token來的,當然不是馬上就放行,而是拿這個token去sso驗證,sso驗證無誤的話,就會記錄下來,剛才那個使用者申請的token是用於訪問server1了,並且把這個使用者的username發給server1。

5.server1收到sso確認的資訊後,根據username把使用者的許可權之類的相關資訊找到,放到session裡面,然後響應使用者請求的資源,並生成一個自己的token給使用者(token不能用sso生成的那個,只能各自用各自的,前面已經說過了)。再往後,使用者有了server1上的token之後,訪問server1的其他資源就沒什麼問題了,和成功登入單個站點一樣的,沒什麼區別;

6.然後,使用者又想訪問server2了,第一次請求server2時,同樣的,由於並沒有server2發的token,server2仍然會重定向使用者到sso去;

7.使用者又去訪問sso,不過這次不太一樣的是,他已經登入過sso了,有了sso發的全域性token,所以這次訪問sso的時候帶上了全域性token;

8.這回sso一看,有token的,又是想去訪問哪個網站了,就又生成一個臨時token給他;

9.使用者拿著這個臨時的token去訪問server2,一樣的,server2也要去驗證,然後……重複。使用者登入過sso後訪問server2時,被重定向了兩次,但是,無論server2還是sso都沒有再response什麼,使用者根本就感覺不到頁面有什麼變化,直接就可以使用了。再訪問其他站點也是一樣,整個過程只需登入一次,這就是單點登入!

解釋一點裡面幾個問題:

1.為什麼sso要發臨時token?為什麼不是sso驗證通過使用者之後,就直接讓子伺服器通過對使用者的驗證?這是因為使用者登入sso的過程是使用者自己登入的,並不是子伺服器server1或server2代替使用者去的,所以sso驗證通過使用者之後不知道通知哪一個伺服器,就算知道,http是無狀態的嘛,還不知道使用者有沒有在用呢,就算有會話,是哪一個會話呢?整 個記憶體中搜索嗎?所以sso不是這樣設計的。另外,如果由伺服器代理使用者去登入的話,那好像不需要什麼驗證中心了,使用者想訪問哪個伺服器,就代理去哪個伺服器登入就完了嘛,而且這樣也不行,使用者並沒有取得對應伺服器上的token啊,就算代理把取得的token給使用者也不行,因為,使用者拿著這個token去訪問的話,不是同一個會話啊,沒用的。除非,除非這個伺服器就老老實實地做代理,使用者要訪問哪個伺服器,它就去登入哪個伺服器,使用者要訪問什麼它就訪問什麼,並且把取得的東西原樣發給使用者。這樣也是可以的,不過這叫代理服務而不叫單點登入了。

2.sso為什麼要登記使用者登入了哪個伺服器?這個是為了後面的登出。很明顯,全域性token的有效期應該是很長的,要不然使用者在其中一個站點上用得好好的,有可能訪問其他站點時又會要求重新登入。那麼全域性token的有效期多長才合適呢?要是使用者刻意儲存了這個token呢,那是不是可能關掉瀏覽器甚至關機了重新開機,只要恢復了這個token那麼就可以再次訪問所有系統(這裡指的惡意)。所以,從安全形度考慮,有登入就要有登出,登出當然不能登出一個全部都登出,所以sso得記錄下使用者登入了哪些系統,使用者登出一個就刪除一個,所有系統都登出完了全域性token就跟著登出。

登出這個環節還沒有理解透,這裡應該有一個機制,就是sso記錄的使用者訪問各個子站點的資訊有效期應該很短,但是活動著的那一個應該只要活動著就一直有效,這樣使用者單點登入後訪問多個站點,但是,只要一小段時間不使用,sso上記錄的資訊就過時,但是沒關係,只要有一個是有效的,全域性token就還在,由於單點登入本身的特性,使用者再訪問其他站點也不會有要求重新登入的問題,但是使用者一直在訪問的使用著的那個站點在sso上的記錄一定不能刪,否則的話再訪問就要重登入,就不是單點登入了。單點登入並不是說要單點一次性全部登入吧。反正每個站點實際上每次接受使用者請求,都要驗證使用者的,我猜是不是在這個時候子站點會每次都向sso發資料,重新整理資訊的。這個只是猜了,估計不同的實現方案就會有不同的方法的吧。前幾天看這方面資料,有人說使用者登出,sso會通知其他伺服器,這個我就不敢苟同了,通知其他伺服器,連線在哪裡,會話在哪裡,就算通知了,伺服器又怎麼知道去登出哪個會話呢?http真的是無狀態的啊。