Django REST framework 之JWT認證


Json Web Token

1、JWT簡介

JWT 是一個開放標准(RFC 7519),它定義了一種用於簡潔,自包含的用於通信雙方之間以 JSON 對象的形式安全傳遞信息的方法。JWT 可以使用 HMAC 算法或者是 RSA 的公鑰密鑰對進行簽名。它具備兩個特點:

  • 簡潔(Compact)

    可以通過URL, POST 參數或者在 HTTP header 發送,因為數據量小,傳輸速度快

  • 自包含(Self-contained)

    負載中包含了所有用戶所需要的信息,避免了多次查詢數據庫

2、JWT 組成

  • Header 頭部

頭部包含了兩部分,token 類型和采用的加密算法

{
  "alg": "HS256",
  "typ": "JWT"
}
  • typ: (Type)類型。在JOSE Header中這是個可選參數,但這里我們需要指明類型是JWT
  • alg: (Algorithm)算法,必須是JWS支持的算法

它會使用 base64url編碼組成 JWT 結構的第一部分

Base64URL 算法

 這個算法跟 Base64 算法基本類似,是一種編碼,也就是說,它是可以被翻譯回原來的樣子來的。它並不是一種加密過程

JWT 作為一個令牌(token),有些場合可能會放到 URL(比如 api.example.com/?token=xxx)。Base64 有三個字符+/=,在 URL 里面有特殊含義,所以要被替換掉:=被省略、+替換成-/替換成_ 。這就是 Base64URL 算法。

  • Payload 負載

這部分就是我們存放信息的地方了,你可以把用戶 ID 等信息放在這里,JWT 規范里面對這部分有進行了比較詳細的介紹,JWT 規定了7個官方字段,供選用

iss (issuer):簽發人
exp (expiration time):過期時間
sub (subject):主題
aud (audience):受眾
nbf (Not Before):生效時間
iat (Issued At):簽發時間
jti (JWT ID):編號

常用的有,

{
    "iss": "lee JWT",
    "iat": 1441593502,
    "exp": 1441594722,
    "aud": "www.example.com",
    "sub": "6465644@163.com"
}

同樣的,它會使用 base64url 編碼組成 JWT 結構的第二部分

  • Signature 簽名

 簽名的作用是保證 JWT 沒有被篡改過

前面兩部分都是使用 base64url 進行編碼的,即前端可以解開知道里面的信息。Signature 需要使用編碼后的 header 和 payload 以及我們提供的一個密鑰,這個密鑰只有服務器才知道,不能泄露給用戶,然后使用 header 中指定的簽名算法(HS256)進行簽名。

HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
 secret)

算出簽名以后,把 Header、Payload、Signature 三個部分拼成一個字符串,每個部分之間用"點"(.)分隔,就可以返回給用戶。

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjU3ZmVmMTY0ZTU0YWY2NGZmYzUzZGJkNSIsInhzcmYiOiI0ZWE1YzUwOGE2NTY2ZTc2MjQwNTQzZjhmZWIwNmZkNDU3Nzc3YmUzOTU0OWM0MDE2NDM2YWZkYTY1ZDIzMzBlIiwiaWF0IjoxNDc2NDI3OTMzfQ.PA3QjeyZSUh7H0GfE0vJaKW4LjKJuC3dVLQiY4hii8s
  • 簽名的目的

  最后一步簽名的過程,實際上是對頭部以及負載內容進行簽名,防止內容被竄改。如果有人對頭部以及負載的內容解碼之后進行修改,再進行編碼,最后加上之前的簽名組合形成新的JWT的話,那么服務器端會判斷出新的頭部和負載形成的簽名和JWT附帶上的簽名是不一樣的。如果要對新的頭部和負載進行簽名,在不知道服務器加密時用的密鑰的話,得出來的簽名也是不一樣的。

3、JWT 的使用方式

客戶端收到服務器返回的 JWT,可以儲存在 Cookie 里面,也可以儲存在 localStorage。

 

此后,客戶端每次與服務器通信,都要帶上這個 JWT。你可以把它放在 Cookie 里面自動發送,但是這樣不能跨域,所以更好的做法是放在 HTTP 請求的頭信息Authorization字段里面。

  1. 首先,前端通過Web表單將自己的用戶名和密碼發送到后端的接口。這一過程一般是一個HTTP POST請求。建議的方式是通過SSL加密的傳輸(https協議),從而避免敏感信息被嗅探。

  2. 后端核對用戶名和密碼成功后,將用戶的id等其他信息作為JWT Payload(負載),將其與頭部分別進行Base64編碼拼接后簽名,形成一個JWT。形成的JWT就是一個形同lll.zzz.xxx的字符串。

  3. 后端將JWT字符串作為登錄成功的返回結果返回給前端。前端可以將返回的結果保存在localStorage或sessionStorage上,退出登錄時前端刪除保存的JWT即可。

  4. 前端在每次請求時將JWT放入HTTP Header中的Authorization位。(解決XSS和XSRF問題)

        5.后端檢查是否存在,如存在驗證JWT的有效性。例如,檢查簽名是否正確;檢查Token是否過期;檢查Token的接收方是否是自己(可選)。

4、JWT 的幾個特點

(1)JWT 默認是不加密,但也是可以加密的。生成原始 Token 以后,可以用密鑰再加密一次。

(2)JWT 不加密的情況下,不能將秘密數據寫入 JWT。

(3)JWT 不僅可以用於認證,也可以用於交換信息。有效使用 JWT,可以降低服務器查詢數據庫的次數。

(4)JWT 的最大缺點是,由於服務器不保存 session 狀態,因此無法在使用過程中廢止某個 token,或者更改 token 的權限。也就是說,一旦 JWT 簽發了,在到期之前就會始終有效,除非服務器部署額外的邏輯。

(5)JWT 本身包含了認證信息,一旦泄露,任何人都可以獲得該令牌的所有權限。為了減少盜用,JWT 的有效期應該設置得比較短。對於一些比較重要的權限,使用時應該再次對用戶進行認證。

(6)為了減少盜用,JWT 不應該使用 HTTP 協議明碼傳輸,要使用 HTTPS 協議傳輸。

Django REST framework 中使用 JWT認證

 使用django-rest-framework-jwt這個庫來幫助我們簡單的使用jwt進行身份驗證
並解決一些前后端分離而產生的跨域問題

  • 安裝

直接使用pip安裝即可,目前支持Python、Django、DRF主流版本

pip install djangorestframework-jwt
  • 使用
在settings.py文件中,將JSONWebTokenAuthentication 添加到REST framework框架的DEFAULT_AUTHENTICATION_CLASSES.
REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': (
        'rest_framework.permissions.IsAuthenticated',
    ),
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
        'rest_framework.authentication.SessionAuthentication',
        'rest_framework.authentication.BasicAuthentication',
    ),

  同樣,你還可以使用基於APIView類的視圖,在每個視圖或每個視圖集的基礎上設置身份驗證方案。與 Token 認證一樣,盡可能使用基於APIView類的視圖認證方式。

但使用基於APIView類的視圖認證方式時,不要忘記導入類。

from rest_framework_jwt.authentication import JSONWebTokenAuthentication

在你的urls.py文件中添加以下URL路由,以便通過POST包含用戶名和密碼的令牌獲取。

from rest_framework_jwt.views import obtain_jwt_token
urlpatterns += [
    url(r'^api-token-auth/', obtain_jwt_token)
]

如果你使用用戶名admin和密碼admin123456創建了用戶,則可以通過在終端中執行以下操作來測試JWT是否正常工作。

$ curl -X POST -d "username=admin&password=admin123456" http://127.0.0.1:8000/api-token-auth/

或者,你可以使用Django REST framework支持的所有內容類型來獲取身份驗證令牌。例如:

$ curl -X POST -H "Content-Type: application/json" -d '{"token":"<EXISTING_TOKEN>"}' http://localhost:8000/api-token-refresh/

可以重復使用令牌刷新(token1 -> token2 -> token3),但此令牌鏈存儲原始令牌(使用用戶名/密碼憑據獲取)的時間。作為orig_iat,你只能將刷新令牌保留至JWT_REFRESH_EXPIRATION_DELTA。

刷新token以獲得新的token的作用在於,持續保持活躍用戶登錄狀態。比如通過用戶密碼獲得的token有效時間為1小時,那么也就意味着1小時后此token失效,用戶必須得重新登錄,這對於活躍用戶來說其實是多余的。如果這個用戶在這1小時內都在瀏覽網站,我們不應該讓用戶重新登錄,就是在token沒有失效之前調用刷新接口為用戶獲得新的token。

  • 認證Token

在一些微服務架構中,身份驗證由單個服務處理。此服務負責其他服務委派確認用戶已登錄此身份驗證服務的責任。這通常意味着其他服務將從用戶接收JWT傳遞給身份驗證服務,並在將受保護資源返回給用戶之前等待JWT有效的確認。添加以下URL模式:

from rest_framework_jwt.views import verify_jwt_token
urlpatterns += [
    url(r'^api-token-verify/', verify_jwt_token)
]

將Token傳遞給驗證API,如果令牌有效,則返回令牌,返回狀態碼為200。否則,它將返回400 Bad Request以及識別令牌無效的錯誤。

  • 手動創建Token

有時候你可能希望手動生成令牌,例如在創建帳戶后立即將令牌返回給用戶。或者,你需要返回的信息不止是Token,可能還有用戶權限相關值。你可以這樣做:

from rest_framework_jwt.settings import api_settings
 
jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
 
payload = jwt_payload_handler(user)
token = jwt_encode_handler(payload)
  • 其他設置

你可以覆蓋一些其他設置,比如變更Token過期時間,以下是所有可用設置的默認值。在settings.py文件中設置。

JWT_AUTH = {
    'JWT_ENCODE_HANDLER':
    'rest_framework_jwt.utils.jwt_encode_handler',
 
    'JWT_DECODE_HANDLER':
    'rest_framework_jwt.utils.jwt_decode_handler',
 
    'JWT_PAYLOAD_HANDLER':
    'rest_framework_jwt.utils.jwt_payload_handler',
 
    'JWT_PAYLOAD_GET_USER_ID_HANDLER':
    'rest_framework_jwt.utils.jwt_get_user_id_from_payload_handler',
 
    'JWT_RESPONSE_PAYLOAD_HANDLER':
    'rest_framework_jwt.utils.jwt_response_payload_handler',
    
    // 這是用於簽署JWT的密鑰,確保這是安全的,不共享不公開的
    'JWT_SECRET_KEY': settings.SECRET_KEY,
    'JWT_GET_USER_SECRET_KEY': None,
    'JWT_PUBLIC_KEY': None,
    'JWT_PRIVATE_KEY': None,
    'JWT_ALGORITHM': 'HS256',
    // 如果秘鑰是錯誤的,它會引發一個jwt.DecodeError
    'JWT_VERIFY': True,
    'JWT_VERIFY_EXPIRATION': True,
    'JWT_LEEWAY': 0,
    // Token過期時間設置
    'JWT_EXPIRATION_DELTA': datetime.timedelta(seconds=300),
    'JWT_AUDIENCE': None,
    'JWT_ISSUER': None,
    
    // 是否開啟允許Token刷新服務,及限制Token刷新間隔時間,從原始Token獲取開始計算
    'JWT_ALLOW_REFRESH': False,
    'JWT_REFRESH_EXPIRATION_DELTA': datetime.timedelta(days=7),
 
    // 定義與令牌一起發送的Authorization標頭值前綴
    'JWT_AUTH_HEADER_PREFIX': 'JWT',
    'JWT_AUTH_COOKIE': None,

一般除了過期時間外,其他配置參數很少改變。具體參數意義當用到時可以查詢官網。

 

參考:http://www.ruanyifeng.com/blog/2018/0

7/json_web_token-tutorial.html

參考:https://juejin.im/entry/5979a9355188253de4272ff4

參考:http://www.ywnds.com/?p=14967


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM