瞭解JWT認證

雲崖先生發表於2020-11-04

JWT介紹

   JWT的全稱為Json web token,是為了在網路應用環境間傳遞宣告而執行的一種基於JSON的開放標準((RFC 7519).該token被設計為緊湊且安全的,特別適用於分散式站點的單點登入(SSO)場景。

   JWT的宣告一般被用來在身份提供者和服務提供者間傳遞被認證的使用者身份資訊,以便於從資源伺服器獲取資源,也可以增加一些額外的其它業務邏輯所必須的宣告資訊,該token也可直接被用於認證,也可被加密。

基本構成

   JWT的本質就是token,它主要有三部分組成,分別是頭部(header)、荷載(payload)主題部、以及簽證(signature)。

   前兩部分都是由base64進行編碼(可反接的加密),後一部分是不可反解的加密,由前兩部分base64的結果加密(hash256)後組成。

   各部分之間用.進行分割,如下所示:

eyJ0eXBlIjogIkpXVCIsICJhbGciOiAiSEFTSDI1NiJ9.7B226964223A202231303031222C20226E616D65223A202279756E7961222C2022616765223A20223138227D.3495bb47139c954ba17c3081262c55d2b436346e0d58990202cc668b7bca54ac

header

   header中一般都具有兩種資訊:

   宣告型別,這裡是jwt

   宣告加密的演算法 通常直接使用 HMAC SHA256

   當然你也可以新增上其他資訊,如公司名稱等。這都是允許的

   下面進行自定義頭部,JSON格式:

{
    "type":"JWT",
    "alg":"HASH256"
}

   然後使用base64對其進行編碼,得到JWT中的header部分:

eyJ0eXBlIjogIkpXVCIsICJhbGciOiAiSEFTSDI1NiJ9

payload

   荷載部作為JWT三部分中的第二部分,都是存放有效資訊。它可以存放三種型別的有效資訊:

   標準中註冊的宣告

   公共的宣告

   私有的宣告

   標準中註冊的宣告(建議但不強制使用):

荷載部位的key描述
iss JWT簽發者(服務端)
sub JWT所面向的使用者
aud 接收JWT的一方
exp JWT的過期時間,該時間必須大於簽發時間
nbf 再某一時間段之前,該JWT不可用
jti JWT的唯一身份標識,主要用作一次性token,迴避時序攻擊

   公共的宣告:

   公共的宣告可以新增任何的資訊,一般新增使用者的相關資訊或其他業務需要的必要資訊,但不建議新增敏感資訊,因為該部分在客戶端可解密。

   私有的宣告:

   私有宣告是提供者和消費者所共同定義的宣告,一般不建議存放敏感資訊,因為base64是對稱解密的,意味著該部分資訊可以歸類為明文資訊。

   如下,定義一個payload

{
    "id":"1001",
    "name":"yunya",
    "age":"12",
}

   然後將其進行base64加密,得到JWT的第二部分。

7B226964223A202231303031222C20226E616D65223A202279756E7961222C2022616765223A20223138227D

signature

   JWT的第三部分是一個簽證資訊,這個簽證資訊由三部分組成

   header (base64後的)

   payload (base64後的)

   secret

   這個部分需要base64加密後的headerbase64加密後的payload使用.連線組成的字串,然後通過header中宣告的加密方式進行加鹽secret組合加密,然後就構成了JWT的第三部分。

signature = hashlib.sha256()
signature.update(header_payload_result)
signature.update("salt".encode("utf-8")) # 加鹽
signature_result = signature.hexdigest()  # 獲得結果

   JWT第三部分結果:

3495bb47139c954ba17c3081262c55d2b436346e0d58990202cc668b7bca54ac

最終的JWT

   下面將用Python進行演示,如何建立一個JWT

import base64
import hashlib
import json


# 第一步:進行頭部宣告
header = {
    "type":"JWT",
    "alg":"HASH256"
}

header_result = base64.b64encode(json.dumps(header).encode("utf-8"))

# 第二步:進行荷載宣告,不要存放密碼等敏感資訊,因為可通過反解出來(你也可以存放過期時間等,都是OK的)
payload = {
    "id":"1001",
    "name":"yunya",
    "age":"18",
}

payload_result = base64.b16encode(json.dumps(payload).encode("utf-8"))

# 第三步:將頭部與荷載進行 . 拼接
header_payload_result = header_result + b"." + payload_result


# 第四步:將頭部與荷載的資訊與鹽進行hash256加密(通常加密方式都是再alg中宣告的),得到簽證
signature = hashlib.sha256()
signature.update(header_payload_result)
signature.update("salt".encode("utf-8")) # 加鹽
signature_result = signature.hexdigest()  # 獲得結果

# 第五步:通過 . 拼接出jwt
jwt = header_payload_result + b"." + signature_result.encode("utf-8")
print(jwt)

# b'eyJ0eXBlIjogIkpXVCIsICJhbGciOiAiSEFTSDI1NiJ9.7B226964223A202231303031222C20226E616D65223A202279756E7961222C2022616765223A20223138227D.3495bb47139c954ba17c3081262c55d2b436346e0d58990202cc668b7bca54ac'

驗證流程

   下面是JWT的驗證流程:

   瞭解JWT認證

   再附上一張原生token驗證的流程:

   image-20200917011529551

優勢所在

   要想了解JWT的優勢,則需要與cookie以及session做對比。

   cookie的劣勢主要是儲存時不安全,所有資料存放只使用者本地,一旦被竊取就可以偽造登入。

   session的劣勢主要有三點:①.資料存放至伺服器,佔用伺服器資源。②.每次使用者登入成功後,都需要向資料庫中寫入session,速度緩慢。③.對於叢集式的部署,如果儲存session的資料庫不一致,則會是個大麻煩,因為使用者如果接入了不同的伺服器,則意味著寫入的session也在不同的資料庫中。這會導致使用者的session在不同資料庫中會存在多次寫入的問題。

   瞭解了cookie以及session的劣勢後,jwt的優勢就顯而易見。

   使用者資料存放至本地,但必須要與服務端儲存的鹽進行對比一致後才認證成功。

   不需要有資料庫寫入的操作。

   叢集式部署時也沒有任何問題,前提是每個伺服器的鹽都一樣。

   點我瞭解更多

相關文章