【技術博客】JWT -- JSON WEB TOKEN

Json web token (JWT),是爲了在網絡應用環境間傳遞聲明而執行的一種基於 JSON 的開放標準(RFC 7519).該 token 被設計爲緊湊且安全的,特別適用於分佈式站點的單點登陸(SSO)場景。JWT 的聲明通常被用來在身份提供者和服務提供者間傳遞被認證的用戶身份信息,以便於從資源服務器獲取資源,也能夠增長一些額外的其它業務邏輯所必須的聲明信息,該 token 也可直接被用於認證,也可被加密。python

從 HTTP 談起

HTTP 是一個無狀態的應用層協議。web

  • 無狀態意味着每一個請求都是獨立的。
  • 每次請求都不不清楚以前的請求發生了什麼。

比如咱們在淘寶上網購:redis

  1. 登陸淘寶
  2. 下單購買
  3. 支付金額

然而,由於 HTTP 是無狀態的,咱們在下單購買的時候,服務器此時已經不記得咱們的身份信息了,須要從新進行身份認證。算法

傳統跨域登陸認證方式

傳統的登陸校驗採用的是 cookie-seession 方式,它的流程通常以下:django

  1. 客戶端使用用戶名和密碼向服務器請求登陸。
  2. 服務器驗證成功後,在當前對話(session)裏面保存相關的數據(用戶 UID、登陸時間等)。
  3. 服務器向用戶返回一個 session_id,寫入用戶的 cookie。
  4. 用戶以後的每一次請求,都會經過 cookie,將 session_id 提交給服務器。
  5. 服務器收到 session_id 後,查找以前保存的對話數據,獲取用戶身份,從而判斷用戶是否已經完成登陸校驗。

傳統模式的缺點是可擴展性差。對於單機操做影響不大,但若是是服務器集羣,或者是跨域的服務導向架構,就要求 session 數據共享,每臺服務器都可以讀取 session,而 session 通常是採用文件的方式存儲,這會帶來文件同步問題和文件讀取問題。即使將 session 放入到 redis 中,也沒法避免高併發數據的讀取問題。json

JWT 的驗證方式

JWT 採用的是 token 令牌的方式進行校驗:後端

  1. 客戶端使用用戶名和密碼向服務器請求登陸。
  2. 服務器驗證成功後,簽發一個 token 返回給客戶端。
  3. 客戶端收到 token 後將其緩存起來,以後的每次請求都會攜帶該 token。、
  4. 用戶以後的每一次請求,都會經過 cookie,將 session_id 提交給服務器。
  5. 服務器收到請求後,驗證請求中攜帶的 token,驗證經過後進行業務邏輯處理。

這樣作的優勢在於:api

  • 服務器不保存任何 session 數據,減小服務器開銷,方便擴展。
  • JWT 構成簡單,只佔用不多的字節,便於傳輸。
  • JSON 格式通用,便於跨語言使用。

一個 JWT 字符串包括如下三個部分:跨域

  • 頭部(header)緩存

    • 標識用於生成簽名的算法,例如:

      {
        ‘typ‘: ‘JWT‘,
        ‘alg‘: ‘HS256‘
      }
    • typ 表示 token 的類型,默認爲 JWT,通常不改動。

    • alg 表示加密算法,默認 HMAC SHA256。

  • 載荷(payload)

    • 存放有效信息:
      • 官方標準的字段申明
      • 公共的字段聲明
      • 私有的字段聲明
  • 簽證(signature)

    • 用密鑰對 header 和 playload 兩部分進行簽證,防止數據被篡改。

Django REST framework

咱們的項目後端是基於 Django 進行開發的。

通常而言是在 restful 的接口中使用 JWT token,相關的 Django 庫是 djangorestframeworkdjangorestframework-jwt

首先須要在 settings 中設置 DEFAULT_AUTHENTICATION_CLASSES 來進行用戶認證:

REST_FRAMEWORK = {
    'DEFAULT_SCHEMA_CLASS': (
        'rest_framework.schemas.coreapi.AutoSchema'
    ),
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
        'rest_framework.authentication.BasicAuthentication',
    )
}

接着設置 JWT_AUTH 來進行 token 的刷新:

JWT_AUTH = {
    'JWT_EXPIRATION_DELTA': datetime.timedelta(days=1),
    'JWT_ALLOW_REFRESH': True,
    'JWT_AUTH_HEADER_PREFIX': 'JWT',
}

在用戶登陸時,生成 token:

serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)

user = self.perform_create(serializer)

re_dict = serializer.data
payload = jwt_payload_handler(user)
re_dict['token'] = jwt_encode_handler(payload)
re_dict['username'] = user.username

headers = self.get_success_headers(serializer.data)
return Response(re_dict, status=status.HTTP_201_CREATED, headers=headers)

urls 中註冊路由:

urlpatterns = [
    path('login/', obtain_jwt_token),
    path('refresh/', refresh_jwt_token),
    path('verify/', verify_jwt_token),
]
相關文章
相關標籤/搜索