測試開發進階(三十一)

  • 2019 年 11 月 5 日
  • 筆記

用戶模組

Json Web Token認證

最常見的認證機制

  • Session認證
  • Token認證

Session認證

保持在服務端,增加伺服器開銷

分散式架構中,難以維持Session會話同步

CSRF攻擊風險(跨站請求)

Token認證

保存在客戶端

跨語言,跨平台

擴展性強

鑒權性能高

JWT(Json Web Token)

由三部分組成

  • header

聲明類型

聲明加密演算法

base64加密,可以解密

  • playload

存放過期時間,簽發用戶等

可以添加用戶的非敏感資訊

base64加密,可以解密

  • signature

由三部分組成

使用base64加密之後的header + . + 使用base64加密之後的playload + 使用HS256演算法加密,同時secret加鹽處理

安裝djangorestframework-jwt

$ pip install djangorestframework-jwt

使用

在 setting.py中添加

REST_FRAMEWORK = {    'DEFAULT_AUTHENTICATION_CLASSES': [        # 使用JWT Token認證        'rest_framework_jwt.authentication.JSONWebTokenAuthentication',        # Basic類型的認證(帳號和密碼)        'rest_framework.authentication.SessionAuthentication',        # Session會話認證        'rest_framework.authentication.BasicAuthentication',    ],}

添加路由

用戶處:user/urls.py

from django.urls import pathfrom rest_framework_jwt.views import obtain_jwt_tokenurlpatterns = [    path('login/', obtain_jwt_token),]

主路由:

ApiTest/urls.py

urlpatterns = [    path('users/', include('user.urls'))]

不登錄的訪問

登錄後的返回內容

HTTP 200 OKAllow: POST, OPTIONSContent-Type: application/jsonVary: Accept{    "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6Inpob25neGluIiwiZXhwIjoxNTcyMzA5MzQ2LCJlbWFpbCI6IjQ5MDMzNjUzNEBxcS5jb20ifQ.ZDEeBAgSuPyvh1KBnF1sSY9w22guSRHXm8sbgqEWusg"}
import base64base64.b64decode('eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9')# b'{"typ":"JWT","alg":"HS256"}'

認證過期時間

./site-packages/rest_framework_jwt/settings.py

第40行

'JWT_EXPIRATION_DELTA': datetime.timedelta(seconds=300),

Token默認過期時間5分鐘

自行修改過期時間 ApiTest/settings.py

過期時間為1天

JWT_AUTH = {    'JWT_EXPIRATION_DELTA': datetime.timedelta(days=1)}

請求Token 頭

'JWT_AUTH_HEADER_PREFIX': 'JWT',

使用httpie發起包含token的內容

安裝 httpie-jwt-auth插件

$ export JWT_AUTH_TOKEN='你的token'$ export JWT_AUTH_PREFIX='JWT'$ http -A jwt :8000/projects/ page==2 size==2

修改載荷

def jwt_response_payload_handler(token, user=None, request=None):    """    Returns the response data for both the login and refresh views.    Override to return a custom response such as including the    serialized representation of the User.    Example:    def jwt_response_payload_handler(token, user=None, request=None):        return {            'token': token,            'user': UserSerializer(user, context={'request': request}).data        }    """    return {        'token': token    }

在 utils/jwt_handler.py重寫

def jwt_response_payload_handler(token, user=None, request=None):    return {        'token': token,        'user_id': user.id,        'username': user.username,    }
JWT_AUTH = {    'JWT_EXPIRATION_DELTA': datetime.timedelta(days=1),    'JWT_PAYLOAD_GET_USERNAME_HANDLER': 'utils.jwt_handler.jwt_response_payload_handler'}

Django自帶的用戶模型

django.contrib.auth.models.User

查看settings.py可以發現,默認註冊了 django.contrib.auth

註冊

  • 用戶名(6-20位,不重複)
  • 郵箱(符合郵箱格式)
  • 密碼(6-20位,和確認密碼一致)
  • 確認密碼(6-20位,和密碼一致)

user/serializers.py

from rest_framework import serializersfrom django.contrib.auth.models import Userclass RegisterSerializer(serializers.ModelSerializer):    password_conform = serializers.CharField(label='確認密碼',                                             min_length=6,                                             max_length=20,                                             write_only=True,                                             help_text='確認密碼',                                             error_messages={'min_length': '僅允許6~20個字元的確認密碼',                                                             'max_length': '僅允許6~20個字元的確認密碼'}                                             )    token = serializers.CharField(label='生成token',                                  read_only=True)    class Meta:        model = User        fields = ('id', 'username', 'password', 'email', 'password_conform', 'token')        extra_kwargs = {            'username': {                'label': '用戶名',                'help_text': '用戶名',                'min_length': 6,                'max_length': 20,                'error_messages': {'min_length': '僅允許6~20個字元的用戶名',                                   'max_length': '僅允許6~20個字元的用戶名'}            },            'email': {                'label': '郵箱',                'help_text': '郵箱',                'write_only': True,                'required': True            },            'password': {                'label': '密碼',                'help_text': '密碼',                'write_only': True,                'min_length': 6,                'max_length': 20,                'error_messages': {'min_length': '僅允許6~20個字元的密碼',                                   'max_length': '僅允許6~20個字元的密碼'}            }        }    def create(self, validated_data):        pass

user/urls.py

from django.urls import pathfrom rest_framework_jwt.views import obtain_jwt_tokenfrom . import viewsurlpatterns = [    path('login/', obtain_jwt_token),    path('register/', views.RegisterView.as_view()),]

user/views.py

from rest_framework.generics import CreateAPIViewfrom rest_framework_jwt.authentication import JSONWebTokenAuthenticationfrom user.serializers import RegisterSerializerclass RegisterView(CreateAPIView):    serializer_class = RegisterSerializer    authentication_classes = (JSONWebTokenAuthentication,)

Tags: