測試開發進階(三十一)
- 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): passuser/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,)
