之前對認證這方面的認識一直不太深入,不清楚爲何須要token這種認證,爲何不簡單使用session存儲用戶登陸信息等。最近讀了幾篇大牛的博客纔對認證機制方面有了進一步瞭解。前端
這種認證直接順應HTTP協議的無狀態性,每次執行業務的時候,都暴力地附帶username與password參數,並將其發送給服務器進行驗證。儘管在服務器端能夠優雅地使用AOP技術(如攔截器或動態代理)對全部controller進行前置的登陸驗證。但若是每次驗證都要查數據庫的話,建立鏈接與查詢操做勢必會增大開銷。若是服務器端不作任何記憶(有狀態性)處理的話,那麼這種方式就已經沒有其餘辦法能夠優化了。算法
上面已經點到,只要服務器端稍加一些記憶處理(記錄哪些用戶登陸過)便可大大優化這個過程:只須要在用戶第一次登陸系統的時候,將對應的username放入一個相似與Set<String>的數據結構中。只要登陸一次(保證不退出),那麼當用戶第二次訪問controller的時候,只須要查詢Set<String>中是否有該username便可。但這種方式仍有不足,即每次仍是必需要求客戶端傳username過來,不然服務器端不知道是誰就沒法判斷了。
要優雅地解決上述問題,就要得益於後來HTTP協議中出現的Cookie與Session技術了。當瀏覽器利用HTTP協議訪問服務器的時候,服務器會爲其自動建立一個其獨有的session對象。session在基本的數據結構上相似於鍵值對Map,但不一樣的是它還提供了若干操做方法,且能夠設置時效。既然一個瀏覽器惟一對應了一個session,那就好辦了,用戶第一次登陸驗證成功後,就可把用戶名寫入session中表徵當前處於該瀏覽器上的用戶已經登陸,之後訪問controller只用查session中是否有username鍵便可,如有放行,若沒有則阻止。以下圖所示:spring
目前被衆多公司普遍採用的是token認證,它的認證過程都是圍繞着一個名爲token字符串展開的,其認證流程以下:數據庫
第一次登陸
用戶攜帶username與password請求第一次登陸(爲保證安全性一般採用HTTPS協議傳輸);
服務器接收到後,查詢數據庫看用戶是否存在且密碼(MD5加密後)是否匹配,若匹配,則在用戶表中查詢該用戶信息(角色、權限、狀態等);
從配置文件中讀取簽名的私鑰,並整合上一步獲得的用戶信息生成token(可採用第三方庫JWT lib);
將token寫入cookie並重定向到前端。
登陸後訪問業務
用戶攜帶從cookie(若爲移動終端,能夠是數據庫或文件系統)取出的token訪問需登陸及特定權限的業務;
請求首先被認證攔截器攔截,並獲取到傳來的token值;
根據配置文件中的簽名私鑰,結合JWT lib進行解密與解碼;
驗證簽名是否正確(若不正確JWT會拋出異常)、token是否過時與接收方是不是本身(由本身判斷)等。若經過則證實用戶已登陸,進入權限驗證階段;
經過權限驗證框架(shiro、spring security等)驗證用戶是否具備訪問該業務的權限,如有則返回相應數據。瀏覽器
1.cookie支持問題
session和cookie實際上是緊密相聯的。瀏覽器與服務器首次創建鏈接的時候,服務器會自動生成一個會話號sid,並寫入響應報文的首部字段<Set-cookie>中,返回給客戶端讓其存入cookie。以後每次的HTTP請求報文中均會在首部寫入cookie中的sid,服務器接收到後根據sid取出對應session,再進一步根據username鍵是否存在判斷登陸與否。
能夠看出cookie/session認證要求客戶端必須支持cookie技術,但很顯然,客戶端並非只能爲瀏覽器,還能夠是PC桌面、移動終端等其它平臺,對於這些平臺,咱們沒法保證他們都能支持cookie技術。而token認證只認token這個字符串值,至於前端是瀏覽器採用cookie存的token仍是Android終端用數據庫存的token都無所謂,只要拿到token值便可進行驗證。
2.session共享問題
session是沒法在多臺服務器之間共享的,特別在分佈式部署環境(即多臺部署了同一系統的Web服務器集羣)下將帶來不少同步、一致性問題。好比下面這個場景:
用戶請求登陸,HTTP請求被轉發到了服務器A,在A上完成認證後將登陸狀態記錄到了session;
用戶後續請求其餘需登陸的業務,HTTP請求被轉發到了非A的服務器上,這時因爲這些服務器上的session並不是A上的session,因此其上就沒有登陸狀態記錄,因此業務操做將被拒絕!
很顯然這時採用cookie/session認證就很棘手了,須要本身去維護同步、狀態一致等問題。而token根本不會依賴session,全部服務器都是一致地採用私鑰+解密算法分析簽名的正確性。
3 時間/空間開銷問題
若是session不只是要存儲用戶名,還要存儲時效時間、登陸時間等各類狀態信息(特別是大型系統),那麼一旦登陸的用戶數激增,服務器的內存消耗也將急劇增大。而token認證是徹底將狀態存入了token值中,再利用加解密算法將狀態取出,用時間複雜度換取了空間複雜度,內存開銷大大減少,時間效率有下降。
4 第三方受權問題
採用傳統認證方式,若要訪問業務,必定要先登陸。假如這時一個第三方應用但願獲取該用戶在本系統的一些資源(如頭像、暱稱、簽名等),則必定要先接受登陸攔截器認證纔可放行,這時若是第三方應用也經過用戶名+密碼登陸的形式來獲取信息的話,勢必會暴露用戶在本系統的信息,很不安全!
而一種更巧妙的作法是,先記錄第三方應用的AppID與其url地址,而後跳轉到本系統登陸頁面進行登陸,認證成功後,經本系統的認證服務器生成access_token,攜帶該參數並重定向到url地址所在頁面。此後第三方應用便可憑藉該access_token的權限範圍,訪問所需的本系統的資源。
能夠看出,不管是本系統本身憑藉token訪問本身的資源,仍是第三方應用憑藉access_token訪問本系統資源,依靠的都是token這個憑據,走的都是統一的一套流程,而傳統方式,須要額外寫一套,可維護性很很差。關於第三方認證文章能夠參考理解Ouath2.0。安全
雖然token認證優點很是明顯,但仍然須要考慮以下問題:如何抵禦跨站腳本攻擊(XSS)、如何防範重放攻擊(Replay Attacks)、如何防範MITM (Man-In-The-Middle)攻擊等。對此本文就再也不作詳細敘述了。服務器