【翻譯】REST framework JWT Auth

runstone發表於2019-11-29

原文連結

概述

這個包提供對Django REST frameworkJSON Web Token 認證支援。

需要滿足條件

  • Python (2.7, 3.3, 3.4, 3.5)
  • Django (1.8, 1.9, 1.10)
  • Django REST Framework (3.0, 3.1, 3.2, 3.3, 3.4, 3.5)

安全

與JWT的一些更典型的用法不同,此模組僅生成身份驗證令牌,該身份驗證令牌將驗證請求DRF保護的API資源之一的使用者。實際的請求引數本身不包含在JWT宣告中,這意味著它們沒有被簽名並且可能被篡改。 您僅應通過SSL / TLS公開API端點,以防止內容篡改和某些型別的重放攻擊。

安裝

使用pip方式安裝

$ pip install djangorestframework-jwt

用法

settings.py中,新增JSONWebTokenAuthentication到Django 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',
    ),
}

urls.py中,新增以下URL路由,以允許通過包含使用者名稱和密碼的POST獲得令牌。

from rest_framework_jwt.views import obtain_jwt_token
#...

urlpatterns = [
    '',
    # ...

    url(r'^api-token-auth/', obtain_jwt_token),
]

如果您擁有一個使用者名稱為admin和密碼為password123的使用者,則可以通過在終端中執行以下操作來簡單測試端點是否正常執行。

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

或者,您可以使用Django REST框架支援的所有內容型別來獲取auth令牌。 例如:

$ curl -X POST -H "Content-Type: application/json" -d '{"username":"admin","password":"password123"}' http://localhost:8000/api-token-auth/

您是不是要找: Now in order to access protected api urls you must include the Authorization: JWT <your token> header.

現在,為了訪問受保護的api網址,您必須包含Authorization:JWT <your_token>標頭。

$ curl -H "Authorization: JWT <your_token>" http://localhost:8000/protected-url/

重新整理令牌

如果JWT_ALLOW_REFRESH為True,則可以“重新整理”未過期的令牌以獲得具有更新的過期時間的全新令牌。 新增如下網址格式:

from rest_framework_jwt.views import refresh_jwt_token
#  ...

urlpatterns = [
    #  ...
    url(r'^api-token-refresh/', refresh_jwt_token),
]

如下所示將現有令牌傳遞到重新整理端點:{“ token”:EXISTING_TOKEN}。 請注意,只有未過期的令牌才有效。 JSON響應看起來與正常的獲取令牌端點{“ token”:NEW_TOKEN}相同。

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

可以重複使用令牌重新整理(令牌1->令牌2->令牌3),但是此令牌鏈將原始令牌(使用使用者名稱/密碼憑證獲取)的時間儲存為orig_iat。 您最多隻能將令牌重新整理到JWT_REFRESH_EXPIRATION_DELTA

一個典型的用例是一個Web應用程式,您想讓使用者“登入”該網站而不必重新輸入密碼,或者在令牌過期之前被嚇倒了。 想象一下,他們有一個1個小時的令牌,正好在他們仍在做某事的最後一刻。 使用移動裝置,您也許可以儲存使用者名稱/密碼來獲取新令牌,但這在瀏覽器中並不是一個好主意。 每次使用者載入頁面時,您都可以檢查是否存在現有的未過期令牌,如果該令牌即將過期,請重新整理該令牌以擴充套件會話。 換句話說,如果使用者正在活躍地使用您的網站,則他們可以保持其“會話(session)”有效。

驗證令牌

在某些微服務架構中,身份驗證由單個服務處理。 其他服務委託確認使用者已登入到此身份驗證服務的責任。 這通常意味著服務會將將從使用者收到的JWT傳遞給身份驗證服務,並在將受保護資源返回給使用者之前等待JWT有效的確認。

此軟體包使用驗證端點支援此設定。 新增以下網址格式:

from rest_framework_jwt.views import verify_jwt_token

#...

urlpatterns = [
    #  ...
    url(r'^api-token-verify/', verify_jwt_token),
]

將令牌傳遞到驗證端點將返回200響應,如果令牌有效,則返回令牌。 否則,它將返回一個400 Bad Request(錯誤請求)以及一個指出令牌無效的原因的錯誤。

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

其它配置

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_SECRET_KEY': settings.SECRET_KEY,
    'JWT_GET_USER_SECRET_KEY': None,
    'JWT_PUBLIC_KEY': None,
    'JWT_PRIVATE_KEY': None,
    'JWT_ALGORITHM': 'HS256',
    'JWT_VERIFY': True,
    'JWT_VERIFY_EXPIRATION': True,
    'JWT_LEEWAY': 0,
    'JWT_EXPIRATION_DELTA': datetime.timedelta(seconds=300),
    'JWT_AUDIENCE': None,
    'JWT_ISSUER': None,

    'JWT_ALLOW_REFRESH': False,
    'JWT_REFRESH_EXPIRATION_DELTA': datetime.timedelta(days=7),

    'JWT_AUTH_HEADER_PREFIX': 'JWT',
    'JWT_AUTH_COOKIE': None,

}

該軟體包使用JSON Web令牌Python實現PyJWT,並允許修改其中的一些可用選項。

JWT_SECRET_KEY

這是用於簽署JWT的金鑰。確保這是安全的,並且不會共享或公開。
預設值為專案的settings.SECRET_KEY

JWT_GET_USER_SECRET_KEY

這是JWT_SECRET_KEY的更強大的版本。它是按使用者定義的,因此,如果令牌被盜用,所有者可以輕鬆更改它。更改此值將使給定使用者的所有令牌不可用。值應該是一個函式,僅接受使用者作為引數並返回它的金鑰。
預設為None

JWT_PUBLIC_KEY

這是cryptography.hazmat.primitives.asymmetric.rsa.RSAPublicKey型別的物件。它將用於驗證傳入的JWT的簽名。設定後將覆蓋JWT_SECRET_KEY閱讀文件以獲取更多詳細資訊。請注意,必須將JWT_ALGORITHM設定為RS256RS384RS512之一。
預設為None

JWT_PRIVATE_KEY

這是cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKey型別的物件。它將用於對JWT的簽名元件進行簽名。設定後將覆蓋JWT_SECRET_KEY閱讀文件以獲取更多詳細資訊。請注意,必須將JWT_ALGORITHM設定為RS256RS384RS512之一。
預設為None

JWT_ALGORITHM

可能的值是PyJWT中任何受支援的密碼簽名演算法
預設值為“ HS256”

JWT_VERIFY

如果密碼錯誤,則會引發一個jwt.DecodeError告訴您。您仍然可以通過將JWT_VERIFY設定為False來獲得有效負載。
預設值為True

JWT_VERIFY_EXPIRATION

您可以通過將JWT_VERIFY_EXPIRATION設定為False來關閉到期時間驗證。如果沒有到期驗證,JWT將永遠存在,這意味著攻擊者可以無限期地使用洩露的令牌。
預設值為True

JWT_LEEWAY

這使您可以驗證過去但不是很遠的到期時間。例如,如果您有一個JWT有效負載,其有效時間設定為建立後的30秒,但您知道有時您將在30秒後對其進行處理,則可以將回旋時間設定為10秒,以便有一定的餘量。
預設值為0秒。

JWT_EXPIRATION_DELTA

這是Python的datetime.timedelta的一個例項。這將被新增到datetime.utcnow()來設定到期時間。
預設值為datetime.timedelta(seconds = 300)(5分鐘)

JWT_AUDIENCE

這是一個字串,將根據令牌的aud欄位(如果存在)進行檢查。
預設值為None(如果JWT上出現aud,則失敗)。

JWT_ISSUER

這是一個字串,將根據令牌的iss欄位進行檢查。
預設值為None(不檢查JWT上的iss)。

JWT_ALLOW_REFRESH

啟用令牌重新整理功能。從rest_framework_jwt.views.obtain_jwt_token發行的令牌將具有orig_iat欄位。預設為False

JWT_REFRESH_EXPIRATION_DELTA

令牌重新整理的限制是一個datetime.timedelta例項。這是原始令牌之後可以重新整理未來令牌的時間。
預設值為datetime.timedelta(days = 7)(7天)

JWT_PAYLOAD_HANDLER

指定自定義函式以生成令牌有效載荷。

JWT_PAYLOAD_GET_USER_ID_HANDLER

如果您儲存的user_id與預設的有效負載處理程式不同,請實現此功能以從有效負載中獲取user_id。注意:不推薦使用JWT_PAYLOAD_GET_USERNAME_HANDLER

JWT_PAYLOAD_GET_USERNAME_HANDLER

如果您儲存的username與預設有效負載處理程式不同,請實施此功能以從有效負載中獲取使用者名稱。

JWT_RESPONSE_PAYLOAD_HANDLER

負責控制登入或重新整理後返回的響應資料。重寫以返回自定義響應,例如包括使用者的序列化表示形式。預設返回JWT令牌。

def jwt_response_payload_handler(token, user=None, request=None):
    return {
        'token': token,
        'user': UserSerializer(user, context={'request': request}).data
    }

預設是 {'token': token}

JWT_AUTH_HEADER_PREFIX

您可以修改需要與令牌一起傳送的Authorization標頭值字首。 預設值為JWT。 PR#4中引入了此決定,以允許在DRF中同時使用此程式包和OAuth2。

用於令牌和授權標頭的另一個常見值是Bearer

預設值為JWT

JWT_AUTH_COOKIE

如果除了授權標頭之外還想使用http cookie作為令牌的有效傳輸方式,則可以將其設定為字串。 您在此處設定的字串將用作在請求令牌時將在響應標頭中設定的cookie名稱。 令牌驗證過程還將調查此cookie(如果已設定)。 如果請求中同時包含標頭和cookie,則“授權”標頭具有優先權。

預設值為“None”,建立令牌時不設定cookie,或在驗證令牌時不接受。

擴充套件JSONWebTokenAuthentication

現在,JSONWebTokenAuthentication假定JWT將出現在標頭或cookie(如果已配置)中(請參閱JWT_AUTH_COOKIE)。 JWT規範不需要這樣做(請參閱:進行服務呼叫)。 例如,JWT可以出現在查詢字串中。 如果使用者無法設定標頭(例如HTML中的src元素),則需要具有在查詢字串中傳送JWT的功能。

為了實現此功能,使用者可以編寫自定義身份驗證

class JSONWebTokenAuthenticationQS(BaseJSONWebTokenAuthentication):
    def get_jwt_value(self, request):
         return request.QUERY_PARAMS.get('jwt')

建議使用BaseJSONWebTokenAuthentication,這是一個新的基類,沒有解析HTTP標頭的邏輯。

手動建立新令牌

有時您可能需要手動生成令牌,例如在建立帳戶後立即將令牌返回給使用者。 您可以按照以下步驟進行操作:

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)

相關文章