1. 程式人生 > 實用技巧 >踩坑drf中的jwt

踩坑drf中的jwt

JWT使用方式

關於jwt的三個部分,我這裡不介紹了,我們看看JWT的使用方式:

  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的接收方是否是自己(可選),如果簽名驗證通過,我們也可以獲得此JWT所屬的使用者。

Django REST framework 中使用JWT認證

準備工作

1.安裝

pip install djangorestframework-jwt

2.settings.py配置:我這裡沒有配置全域性的許可權與認證,等會在單獨的介面單獨配置;另外配置JWT時效與JWT字串的字首;最後設定驗證類,主要是驗證類中的authenticate方法。

REST_FRAMEWORK = {
    # 設定所有介面都需要被驗證
    'DEFAULT_PERMISSION_CLASSES': (
        #’rest_framework.permissions.IsAuthenticatedOrReadOnly’,
    ),
    # 使用者登陸認證方式
    'DEFAULT_AUTHENTICATION_CLASSES': (
        # 'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
        #’rest_framework.authentication.SessionAuthentication’,
#’rest_framework.authentication.BasicAuthentication’, ), } # jwt載荷中的有效期設定 JWT_AUTH = { #token 有效期 'JWT_EXPIRATION_DELTA': datetime.timedelta(seconds=1800), 'JWT_AUTH_HEADER_PREFIX': 'JWT', } AUTHENTICATION_BACKENDS = ( #選擇django自己的驗證類 'django.contrib.auth.backends.ModelBackend', )

快速開始

urls.py

from django.contrib import admin
from django.urls import path
from django.conf.urls import url
from rest_framework_jwt.views import obtain_jwt_token

urlpatterns = [
path('admin/', admin.site.urls),
   #登入成功自動簽發token
url(r'^login/', obtain_jwt_token)
]

這裡面主要是obtain_jwt_token,實踐呼叫ObtainJSONWebToken.as_view(),ObtainJSONWebToken類下面,有如下serializer_class = JSONWebTokenSerializer

而JSONWebTokenSerializer就是最重要的類,後面如果你的登入不只有username,password時,需要傳入更多的引數,需要更改這兩個方法。

  • __init__方法序列化前端傳遞的username與password,
  • validate方法通過authenticate方法驗證username與password是否存在 你的 user 表裡,通過驗證則簽發token

POSTMAN測試:注意首先確保你的這個使用者已經存在

如何給介面加上JWT驗證

views.py

from rest_framework.views import APIView
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
from rest_framework import authentication
from rest_framework.response import Response

class IndexViewset(APIView):
  #單獨給這個介面增加JWT認證
    authentication_classes = (JSONWebTokenAuthentication, authentication.SessionAuthentication)
    def get(self, request, *args, **kwargs):
        return Response('POST請求,響應內容')
    def post(self, request, *args, **kwargs):
        return Response('POST請求,響應內容')

上面程式碼中,通過驗證則代表JWT有效,那麼如何獲取到此token所屬於的使用者,看如下程式碼:

from rest_framework.views import APIView
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
from rest_framework import authentication
from rest_framework.response import Response
from rest_framework_jwt.utils import jwt_decode_handler

class IndexViewset(APIView):

    authentication_classes = (JSONWebTokenAuthentication, authentication.SessionAuthentication)

    def get(self, request, *args, **kwargs): 
        print("驗證後的token:",bytes.decode(request.auth))
        token_user = jwt_decode_handler(bytes.decode(request.auth))
        print(token_user['user_id'])
        return Response('POST請求,響應內容')

    def post(self, request, *args, **kwargs):
        return Response('POST請求,響應內容')

特殊情況

1.如果你想擴充套件django user欄位,新建UserProfile類,繼承AbstractUser,settings.py裡指定user model,並遷移資料庫,請檢視

2.如果使用者登入中不僅有使用者名稱,密碼,還有驗證碼等,就需要重寫驗證類,新建CustomBackend類,繼承ModelBackend,實現authenticate方法,settings.py裡指定
AUTHENTICATION_BACKENDS = (
  #選擇django自己的驗證類
  users.views.CustomBackend',
  #選擇django自己的驗證類
  #'django.contrib.auth.backends.ModelBackend',
)

  • 修改\Lib\site-packages\django\contrib\auth\__init__.py,替換authenticate。
def authenticate(request=None, **credentials):
    """
    If the given credentials are valid, return a User object.
    """
    for backend, backend_path in _get_backends(return_tuples=True):
        try:
            inspect.getcallargs(backend.authenticate, request, **credentials)
        except TypeError:
            args = ()
            credentials.pop('request', None)
            # Does the backend accept a request keyword argument?
            try:
                inspect.getcallargs(backend.authenticate, request=request, **credentials)
            except TypeError:
                # Does the backend accept credentials without request?
                try:
                    inspect.getcallargs(backend.authenticate, **credentials)
                except TypeError:
                    # This backend doesn't accept these credentials as arguments. Try the next one.
                    return None
                else:
                    warnings.warn(
                        "Update %s.authenticate() to accept a positional "
                        "`request` argument." % backend_path
                    )
            else:
                credentials['request'] = request
                warnings.warn(
                    "In %s.authenticate(), move the `request` keyword argument "
                    "to the first positional argument." % backend_path
                )
        # Annotate the user object with the path of the backend.
        return backend.authenticate(*args, **credentials)

    # The credentials supplied are invalid to all backends, fire signal

    user_login_failed.send(sender=__name__, credentials=_clean_credentials(credentials), request=request)
View Code
  • 修改ObtainJSONWebToken下的__init__方法與validate。