背景知識:Authentication和Authorization的區(qū)別:Authentication:用戶認證,指的是驗證用戶的身份,例如你希望以小A的身份登錄,那么應(yīng)用程序需要通過用戶名和密碼確認你真的是小A。 Authorization:授權(quán),指的是確認你的身份之后提供給你權(quán)限,例如用戶小A可以修改數(shù)據(jù),而用戶小B只能閱讀數(shù)據(jù)。 由于http協(xié)議是無狀態(tài)的,每一次請求都無狀態(tài)。當一個用戶通過用戶名和密碼登錄了之后,他的下一個請求不會攜帶任何狀態(tài),應(yīng)用程序無法知道他的身份,那就必須重新認證。因此我們希望用戶登錄成功之后的每一次http請求,都能夠保存他的登錄狀態(tài)。 目前主流的用戶認證方法有基于token和基于session兩種方式。
基于session的用戶認證基于session的認證流程如下:
基于token(令牌)的用戶認證最常用的是JSON Web Token(jwt):
jwt的組成jwt的認證原理:一個jwt實際上就是一個字符串,它由三部分組成,頭部、載荷與簽名,這三個部分都是json格式。 頭部(Header)頭部用于描述關(guān)于該JWT的最基本的信息,例如其類型以及簽名所用的算法等。
在這里,我們說明了這是一個JWT,并且我們所用的簽名算法是HS256算法。 載荷(Payload)載荷可以用來放一些不敏感的信息。
這里面的前五個字段都是由JWT的標準所定義的。
把頭部和載荷分別進行Base64編碼之后得到兩個字符串,然后再將這兩個編碼后的字符串用英文句號.連接在一起(頭部在前),形成新的字符串:
簽名(signature)最后,我們將上面拼接完的字符串用HS256算法進行加密。在加密的時候,我們還需要提供一個密鑰(secret)。加密后的內(nèi)容也是一個字符串,最后這個字符串就是簽名,把這個簽名拼接在剛才的字符串后面就能得到完整的jwt。 header部分和payload部分如果被篡改,由于篡改者不知道密鑰是什么,也無法生成新的signature部分,服務(wù)端也就無法通過,在jwt中,消息體是透明的,使用簽名可以保證消息不被篡改。
區(qū)別和優(yōu)缺點:基于session和基于jwt的方式的主要區(qū)別就是用戶的狀態(tài)保存的位置,session是保存在服務(wù)端的,而jwt是保存在客戶端的。 jwt的優(yōu)點:可擴展性好 應(yīng)用程序分布式部署的情況下,session需要做多機數(shù)據(jù)共享,通??梢源嬖跀?shù)據(jù)庫或者redis里面。而jwt不需要。 無狀態(tài) jwt不在服務(wù)端存儲任何狀態(tài)。RESTful API的原則之一是無狀態(tài),發(fā)出請求時,總會返回帶有參數(shù)的響應(yīng),不會產(chǎn)生附加影響。用戶的認證狀態(tài)引入這種附加影響,這破壞了這一原則。另外jwt的載荷中可以存儲一些常用信息,用于交換信息,有效地使用 JWT,可以降低服務(wù)器查詢數(shù)據(jù)庫的次數(shù)。 jwt的缺點:安全性 由于jwt的payload是使用base64編碼的,并沒有加密,因此jwt中不能存儲敏感數(shù)據(jù)。而session的信息是存在服務(wù)端的,相對來說更安全。 性能 jwt太長。由于是無狀態(tài)使用JWT,所有的數(shù)據(jù)都被放到JWT里,如果還要進行一些數(shù)據(jù)交換,那載荷會更大,經(jīng)過編碼之后導(dǎo)致jwt非常長,cookie的限制大小一般是4k,cookie很可能放不下,所以jwt一般放在local storage里面。 并且用戶在系統(tǒng)中的每一次http請求都會把jwt攜帶在Header里面,http請求的Header可能比Body還要大。而sessionId只是很短的一個字符串,因此使用jwt的http請求比使用session的開銷大得多。 一次性 無狀態(tài)是jwt的特點,但也導(dǎo)致了這個問題,jwt是一次性的。想修改里面的內(nèi)容,就必須簽發(fā)一個新的jwt。 (1)無法廢棄 通過上面jwt的驗證機制可以看出來,一旦簽發(fā)一個jwt,在到期之前就會始終有效,無法中途廢棄。例如你在payload中存儲了一些信息,當信息需要更新時,則重新簽發(fā)一個jwt,但是由于舊的jwt還沒過期,拿著這個舊的jwt依舊可以登錄,那登錄后服務(wù)端從jwt中拿到的信息就是過時的。為了解決這個問題,我們就需要在服務(wù)端部署額外的邏輯,例如設(shè)置一個黑名單,一旦簽發(fā)了新的jwt,那么舊的就加入黑名單(比如存到redis里面),避免被再次使用。 (2)續(xù)簽 如果你使用jwt做會話管理,傳統(tǒng)的cookie續(xù)簽方案一般都是框架自帶的,session有效期30分鐘,30分鐘內(nèi)如果有訪問,有效期被刷新至30分鐘。一樣的道理,要改變jwt的有效時間,就要簽發(fā)新的jwt。最簡單的一種方式是每次請求刷新jwt,即每個http請求都返回一個新的jwt。這個方法不僅暴力不優(yōu)雅,而且每次請求都要做jwt的加密解密,會帶來性能問題。另一種方法是在redis中單獨為每個jwt設(shè)置過期時間,每次訪問時刷新jwt的過期時間。 可以看出想要破解jwt一次性的特性,就需要在服務(wù)端存儲jwt的狀態(tài)。但是引入 redis 之后,就把無狀態(tài)的jwt硬生生變成了有狀態(tài)了,違背了jwt的初衷。而且這個方案和session都差不多了。 總結(jié)適合使用jwt的場景:
比如,用戶注冊后發(fā)一封郵件讓其激活賬戶,通常郵件中需要有一個鏈接,這個鏈接需要具備以下的特性:能夠標識用戶,該鏈接具有時效性(通常只允許幾小時之內(nèi)激活),不能被篡改以激活其他可能的賬戶,一次性的。這種場景就適合使用jwt。 而由于jwt具有一次性的特性。單點登錄和會話管理非常不適合用jwt,如果在服務(wù)端部署額外的邏輯存儲jwt的狀態(tài),那還不如使用session?;趕ession有很多成熟的框架可以開箱即用,但是用jwt還要自己實現(xiàn)邏輯。 |
|