3種web會話管理的方式

http是無狀態的,一次請求結束,鏈接斷開,下次服務器再收到請求,它就不知道這個請求是哪一個用戶發過來的。固然它知道是哪一個客戶端地址發過來的,可是對於咱們的應用來講,咱們是靠用戶來管理,而不是靠客戶端。因此對咱們的應用而言,它是須要有狀態管理的,以便服務端可以準確的知道http請求是哪一個用戶發起的,從而判斷他是否有權限繼續這個請求。這個過程就是常說的會話管理。它也能夠簡單理解爲一個用戶從登陸到退出應用的一段期間。本文總結了3種常見的實現web應用會話管理的方式:php

1)基於server端session的管理方式html

2)cookie-base的管理方式前端

3)token-base的管理方式java

這些內容能夠幫助加深對web中用戶登陸機制的理解,對實際項目開發也有參考價值,歡迎閱讀與指正。web

1. 基於server端session的管理

在早期web應用中,一般使用服務端session來管理用戶的會話。快速瞭解服務端session:ajax

1) 服務端session是用戶第一次訪問應用時,服務器就會建立的對象,表明用戶的一次會話過程,能夠用來存放數據。服務器爲每個session都分配一個惟一的sessionid,以保證每一個用戶都有一個不一樣的session對象。redis

2)服務器在建立完session後,會把sessionid經過cookie返回給用戶所在的瀏覽器,這樣當用戶第二次及之後向服務器發送請求的時候,就會經過cookie把sessionid傳回給服務器,以便服務器可以根據sessionid找到與該用戶對應的session對象。算法

3)session一般有失效時間的設定,好比2個小時。當失效時間到,服務器會銷燬以前的session,並建立新的session返回給用戶。可是隻要用戶在失效時間內,有發送新的請求給服務器,一般服務器都會把他對應的session的失效時間根據當前的請求時間再延長2個小時。express

4)session在一開始並不具有會話管理的做用。它只有在用戶登陸認證成功以後,而且往sesssion對象裏面放入了用戶登陸成功的憑證,才能用來管理會話。管理會話的邏輯也很簡單,只要拿到用戶的session對象,看它裏面有沒有登陸成功的憑證,就能判斷這個用戶是否已經登陸。當用戶主動退出的時候,會把它的session對象裏的登陸憑證清掉。因此在用戶登陸前或退出後或者session對象失效時,確定都是拿不到須要的登陸憑證的。json

以上過程可簡單使用流程圖描述以下:
image

主流的web開發平臺(java,.net,php)都原生支持這種會話管理的方式,並且開發起來很簡單,相信大部分後端開發人員在入門的時候都瞭解並使用過它。它還有一個比較大的優勢就是安全性好,由於在瀏覽器端與服務器端保持會話狀態的媒介始終只是一個sessionid串,只要這個串夠隨機,攻擊者就不能輕易冒充他人的sessionid進行操做;除非經過CSRF或http劫持的方式,纔有可能冒充別人進行操做;即便冒充成功,也必須被冒充的用戶session裏面包含有效的登陸憑證才行。可是在真正決定用它管理會話以前,也得根據本身的應用狀況考慮如下幾個問題:

1)這種方式將會話信息存儲在web服務器裏面,因此在用戶同時在線量比較多時,這些會話信息會佔據比較多的內存;

2)當應用採用集羣部署的時候,會遇到多臺web服務器之間如何作session共享的問題。由於session是由單個服務器建立的,可是處理用戶請求的服務器不必定是那個建立session的服務器,這樣他就拿不到以前已經放入到session中的登陸憑證之類的信息了;

3)多個應用要共享session時,除了以上問題,還會遇到跨域問題,由於不一樣的應用可能部署的主機不同,須要在各個應用作好cookie跨域的處理。

針對問題1和問題2,我見過的解決方案是採用redis這種中間服務器來管理session的增刪改查,一來減輕web服務器的負擔,二來解決不一樣web服務器共享session的問題。針對問題3,因爲服務端的session依賴cookie來傳遞sessionid,因此在實際項目中,只要解決各個項目裏面如何實現sessionid的cookie跨域訪問便可,這個是能夠實現的,就是比較麻煩,先後端有可能都要作處理。

若是不考慮以上三個問題,這種管理方式比較值得使用,尤爲是一些小型的web應用。可是一旦應用未來有擴展的必要,那就得謹慎對待前面的三個問題。若是真要在項目中使用這種方式,推薦結合單點登陸框架如CAS一塊兒用,這樣會使應用的擴展性更強。

2. cookie-based的管理方式

因爲前一種方式會增長服務器的負擔和架構的複雜性,因此後來就有人想出直接把用戶的登陸憑證直接存到客戶端的方案,當用戶登陸成功以後,把登陸憑證寫到cookie裏面,並給cookie設置有效期,後續請求直接驗證存有登陸憑證的cookie是否存在以及憑證是否有效,便可判斷用戶的登陸狀態。使用它來實現會話管理的總體流程以下:

1)用戶發起登陸請求,服務端根據傳入的用戶密碼之類的身份信息,驗證用戶是否知足登陸條件,若是知足,就根據用戶信息建立一個登陸憑證,這個登陸憑證簡單來講就是一個對象,最簡單的形式能夠只包含用戶id,憑證建立時間和過時時間三個值。

2)服務端把上一步建立好的登陸憑證,先對它作數字簽名,而後再用對稱加密算法作加密處理,將簽名、加密後的字串,寫入cookie。cookie的名字必須固定(如ticket),由於後面再獲取的時候,還得根據這個名字來獲取cookie值。這一步添加數字簽名的目的是防止登陸憑證裏的信息被篡改,由於一旦信息被篡改,那麼下一步作簽名驗證的時候確定會失敗。作加密的目的,是防止cookie被別人截取的時候,沒法輕易讀到其中的用戶信息。

3)用戶登陸後發起後續請求,服務端根據上一步存登陸憑證的cookie名字,獲取到相關的cookie值。而後先作解密處理,再作數字簽名的認證,若是這兩步都失敗,說明這個登陸憑證非法;若是這兩步成功,接着就能夠拿到原始存入的登陸憑證了。而後用這個憑證的過時時間和當前時間作對比,判斷憑證是否過時,若是過時,就須要用戶再從新登陸;若是未過時,則容許請求繼續。

image

這種方式最大的優勢就是實現了服務端的無狀態化,完全移除了服務端對會話的管理的邏輯,服務端只須要負責建立和驗證登陸cookie便可,無需保持用戶的狀態信息。對於第一種方式的第二個問題,用戶會話信息共享的問題,它也能很好解決:由於若是隻是同一個應用作集羣部署,因爲驗證登陸憑證的代碼都是同樣的,因此無論是哪一個服務器處理用戶請求,總能拿到cookie中的登陸憑證來進行驗證;若是是不一樣的應用,只要每一個應用都包含相同的登陸邏輯,那麼他們也是能輕易實現會話共享的,不過這種狀況下,登陸邏輯裏面數字簽名以及加密解密要用到的密鑰文件或者密鑰串,須要在不一樣的應用裏面共享,總而言之,就是須要算法徹底保持一致。

這種方式因爲把登陸憑證直接存放客戶端,而且須要cookie傳來傳去,因此它的缺點也比較明顯:

1)cookie有大小限制,存儲不了太多數據,因此要是登陸憑證存的消息過多,致使加密簽名後的串太長,就會引起別的問題,好比其它業務場景須要cookie的時候,就有可能沒那麼多空間可用了;因此用的時候得謹慎,得觀察實際的登陸cookie的大小;好比太長,就要考慮是非是數字簽名的算法太嚴格,致使簽名後的串太長,那就適當調整簽名邏輯;好比若是一開始用4096位的RSA算法作數字簽名,能夠考慮換成102四、2048位;

2)每次傳送cookie,增長了請求的數量,對訪問性能也有影響;

3)也有跨域問題,畢竟仍是要用cookie。

相比起第一種方式,cookie-based方案明顯仍是要好一些,目前好多web開發平臺或框架都默認使用這種方式來作會話管理,好比php裏面yii框架,這是咱們團隊後端目前用的,它用的就是這個方案,以上提到的那些登陸邏輯,框架也都已經封裝好了,實際用起來也很簡單;asp.net裏面forms身份認證,也是這個思路,這裏有一篇好文章把它的實現細節都說的很清楚:

http://www.cnblogs.com/fish-li/archive/2012/04/15/2450571.html

前面兩種會話管理方式由於都用到cookie,不適合用在native app裏面:native app很差管理cookie,畢竟它不是瀏覽器。這兩種方案都不適合用來作純api服務的登陸認證。要實現api服務的登陸認證,就要考慮下面要介紹的第三種會話管理方式。

3. token-based的管理方式

這種方式從流程和實現上來講,跟cookie-based的方式沒有太多區別,只不過cookie-based裏面寫到cookie裏面的ticket在這種方式下稱爲token,這個token在返回給客戶端以後,後續請求都必須經過url參數或者是http header的形式,主動帶上token,這樣服務端接收到請求以後就能直接從http header或者url裏面取到token進行驗證:

image

這種方式不經過cookie進行token的傳遞,而是每次請求的時候,主動把token加到http header裏面或者url後面,因此即便在native app裏面也能使用它來調用咱們經過web發佈的api接口。app裏面還要作兩件事情:

1)有效存儲token,得保證每次調接口的時候都能從同一個位置拿到同一個token;

2)每次調接口的的代碼裏都得把token加到header或者接口地址裏面。

看起來麻煩,其實也不麻煩,這兩件事情,對於app來講,很容易作到,只要對接口調用的模塊稍加封裝便可。

這種方式一樣適用於網頁應用,token能夠存於localStorage或者sessionStorage裏面,而後每發ajax請求的時候,都把token拿出來放到ajax請求的header裏便可。不過若是是非接口的請求,好比直接經過點擊連接請求一個頁面這種,是沒法自動帶上token的。因此這種方式也僅限於走純接口的web應用。

這種方式用在web應用裏也有跨域的問題,好比應用若是部署在a.com,api服務部署在b.com,從a.com裏面發出ajax請求到b.com,默認狀況下是會報跨域錯誤的,這種問題能夠用CORS(跨域資源共享)的方式來快速解決,相關細節可去閱讀前面給出的CORS文章詳細瞭解。

這種方式跟cookie-based的方式一樣都還有的一個問題就是ticket或者token刷新的問題。有的產品裏面,你確定不但願用戶登陸後,操做了半個小時,結果ticket或者token到了過時時間,而後用戶又得去從新登陸的狀況出現。這個時候就得考慮ticket或token的自動刷新的問題,簡單來講,能夠在驗證ticket或token有效以後,自動把ticket或token的失效時間延長,而後把它再返回給客戶端;客戶端若是檢測到服務器有返回新的ticket或token,就替換原來的ticket或token。

4. 安全問題

在web應用裏面,會話管理的安全性始終是最重要的安全問題,這個對用戶的影響極大。

首先從會話管理憑證來講,第一種方式的會話憑證僅僅是一個session id,因此只要這個session id足夠隨機,而不是一個自增的數字id值,那麼其它人就不可能輕易地冒充別人的session id進行操做;第二種方式的憑證(ticket)以及第三種方式的憑證(token)都是一個在服務端作了數字簽名,和加密處理的串,因此只要密鑰不泄露,別人也沒法輕易地拿到這個串中的有效信息並對它進行篡改。總之,這三種會話管理方式的憑證自己是比較安全的。

而後從客戶端和服務端的http過程來講,當別人截獲到客戶端請求中的會話憑證,就能拿這個憑證冒充原用戶,作一些非法操做,而服務器也認不出來。這種安全問題,能夠簡單採用https來解決,雖然可能還有http劫持這種更高程度的威脅存在,可是咱們從代碼能作的防範,確實也就是這個層次了。

最後的安全問題就是CSRF(跨站請求僞造)。這個跟代碼有很大關係,本質上它就是代碼的漏洞,只不過通常狀況下這些漏洞,做爲開發人員都不容易發現,只有那些一門心思想搞些事情的人才會專門去找這些漏洞,因此這種問題的防範更多地仍是依賴於開發人員對這種攻擊方式的瞭解,包括常見的攻擊形式和應對方法。無論憑證信息自己多麼安全,別人利用CSRF,就能拿到別人的憑證,而後用它冒充別人進行非法操做,因此有時間還真得多去了解下它的相關資料才行。舉例來講,假如咱們把憑證直接放到url後面進行傳遞,就有可能成爲一個CSRF的漏洞:當惡意用戶在咱們的應用內上傳了1張引用了他本身網站的圖片,當正常的用戶登陸以後訪問的頁面裏面包含這個圖片的時候,因爲這個圖片加載的時候會向惡意網站發送get請求;當惡意網站收到請求的時候,就會從這個請求的Reffer header裏面看到包含這個圖片的頁面地址,而這個地址正好包含了正經常使用戶的會話憑證;因而惡意用戶就拿到了正經常使用戶的憑證;只要這個憑證還沒失效,他就能用它冒充用戶進行非法操做。

5. 總結

前面這三種方式,各自有各自的優勢及使用場景,我以爲沒有哪一個是最好的,作項目的時候,根據項目未來的擴展狀況和架構狀況,才能決定用哪一個是最合適的。本文的目的也就是想介紹這幾種方式的原理,以便掌握web應用中登陸驗證的關鍵因素。

做爲一個前端開發人員,本文雖然介紹了3種會話管理的方式,可是與前端關係最緊密的仍是第三種方式,畢竟如今前端開發SPA應用以及hybrid應用已經很是流行了,因此掌握好這個方式的認證過程和使用方式,對前端來講,顯然是頗有幫助的。好在這個方式的技術其實早就有不少實現了,並且還有現成的標準可用,這個標準就是JWT(json-web-token)。

JWT自己並無作任何技術實現,它只是定義了token-based的管理方式該如何實現,它規定了token的應該包含的標準內容以及token的生成過程和方法。目前實現了這個標準的技術已經有很是多:

image

更多可參閱:https://jwt.io/#libraries-io

爲了對第三種會話管理方式的實現有個更全面的認識,我選擇用express和上面衆多JWT實現中的jsonwebtoken來研究,相關內容我會在下一篇博客詳細介紹。本文內容到此結束,謝謝閱讀,歡迎關注下一篇博客的內容。

相關文章
相關標籤/搜索