Authentication:用戶認證,指的是驗證用戶的身份,例如你但願以小A的身份登陸,那麼應用程序須要經過用戶名和密碼確認你真的是小A。redis
Authorization:受權,指的是確認你的身份以後提供給你權限,例如用戶小A能夠修改數據,而用戶小B只能閱讀數據。算法
因爲http協議是無狀態的,每一次請求都無狀態。當一個用戶經過用戶名和密碼登陸了以後,他的下一個請求不會攜帶任何狀態,應用程序沒法知道他的身份,那就必須從新認證。所以咱們但願用戶登陸成功以後的每一次http請求,都可以保存他的登陸狀態。數據庫
目前主流的用戶認證方法有基於token和基於session兩種方式。json
基於session的認證流程以下: 後端
最經常使用的是JSON Web Token(jwt): 瀏覽器
一個jwt實際上就是一個字符串,它由三部分組成,頭部、載荷與簽名,這三個部分都是json格式。安全
頭部用於描述關於該JWT的最基本的信息,例如其類型以及簽名所用的算法等。服務器
{ "typ": "JWT", "alg": "HS256" } 複製代碼
在這裏,咱們說明了這是一個JWT,而且咱們所用的簽名算法是HS256算法。cookie
載荷能夠用來放一些不敏感的信息。session
{ "iss": "John Wu JWT", "iat": 1441593502, "exp": 1441594722, "aud": "www.example.com", "sub": "jrocket@example.com", "from_user": "B", "target_user": "A" } 複製代碼
這裏面的前五個字段都是由JWT的標準所定義的。
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJmcm9tX3VzZXIiOiJCIiwidGFyZ2V0X3VzZXIiOiJBIn0 複製代碼
最後,咱們將上面拼接完的字符串用HS256算法進行加密。在加密的時候,咱們還須要提供一個密鑰(secret)。加密後的內容也是一個字符串,最後這個字符串就是簽名,把這個簽名拼接在剛纔的字符串後面就能獲得完整的jwt。header部分和payload部分若是被篡改,因爲篡改者不知道密鑰是什麼,也沒法生成新的signature部分,服務端也就沒法經過,在jwt中,消息體是透明的,使用簽名能夠保證消息不被篡改。
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJmcm9tX3VzZXIiOiJCIiwidGFyZ2V0X3VzZXIiOiJBIn0.rSWamyAYwuHCo7IFAgd1oRpSP7nzL7BF5t7ItqpKViM 複製代碼
基於session和基於jwt的方式的主要區別就是用戶的狀態保存的位置,session是保存在服務端的,而jwt是保存在客戶端的。
因爲jwt的payload是使用base64編碼的,並無加密,所以jwt中不能存儲敏感數據。而session的信息是存在服務端的,相對來講更安全。
jwt太長。因爲是無狀態使用JWT,全部的數據都被放到JWT裏,若是還要進行一些數據交換,那載荷會更大,通過編碼以後致使jwt很是長,cookie的限制大小通常是4k,cookie極可能放不下,因此jwt通常放在local storage裏面。而且用戶在系統中的每一次http請求都會把jwt攜帶在Header裏面,http請求的Header可能比Body還要大。而sessionId只是很短的一個字符串,所以使用jwt的http請求比使用session的開銷大得多。
無狀態是jwt的特色,但也致使了這個問題,jwt是一次性的。想修改裏面的內容,就必須簽發一個新的jwt。
(1)沒法廢棄 經過上面jwt的驗證機制能夠看出來,一旦簽發一個jwt,在到期以前就會始終有效,沒法中途廢棄。例如你在payload中存儲了一些信息,當信息須要更新時,則從新簽發一個jwt,可是因爲舊的jwt還沒過時,拿着這個舊的jwt依舊能夠登陸,那登陸後服務端從jwt中拿到的信息就是過期的。爲了解決這個問題,咱們就須要在服務端部署額外的邏輯,例如設置一個黑名單,一旦簽發了新的jwt,那麼舊的就加入黑名單(好比存到redis裏面),避免被再次使用。
(2)續簽 若是你使用jwt作會話管理,傳統的cookie續簽方案通常都是框架自帶的,session有效期30分鐘,30分鐘內若是有訪問,有效期被刷新至30分鐘。同樣的道理,要改變jwt的有效時間,就要簽發新的jwt。最簡單的一種方式是每次請求刷新jwt,即每一個http請求都返回一個新的jwt。這個方法不只暴力不優雅,並且每次請求都要作jwt的加密解密,會帶來性能問題。另外一種方法是在redis中單獨爲每一個jwt設置過時時間,每次訪問時刷新jwt的過時時間。
能夠看出想要破解jwt一次性的特性,就須要在服務端存儲jwt的狀態。可是引入 redis 以後,就把無狀態的jwt硬生生變成了有狀態了,違背了jwt的初衷。並且這個方案和session都差很少了。
適合使用jwt的場景:
好比,用戶註冊後發一封郵件讓其激活帳戶,一般郵件中須要有一個連接,這個連接須要具有如下的特性:可以標識用戶,該連接具備時效性(一般只容許幾小時以內激活),不能被篡改以激活其餘可能的帳戶,一次性的。這種場景就適合使用jwt。
而因爲jwt具備一次性的特性。單點登陸和會話管理很是不適合用jwt,若是在服務端部署額外的邏輯存儲jwt的狀態,那還不如使用session。基於session有不少成熟的框架能夠開箱即用,可是用jwt還要本身實現邏輯。