首
前后端分离的项目更多使用JWT认证——Json Web Token。本文记录djangorestframework-simplejwt的使用方式。文档
安装
pip install djangorestframework-simplejwt
配置settings.py:
INSTALLED_APPS = ['rest_framework_simplejwt',
]REST_FRAMEWORK = {'DEFAULT_AUTHENTICATION_CLASSES': ('rest_framework_simplejwt.authentication.JWTAuthentication',)
}from datetime import timedeltaSIMPLE_JWT = {"ACCESS_TOKEN_LIFETIME": timedelta(minutes=5), # access有效时间"REFRESH_TOKEN_LIFETIME": timedelta(days=1), # refresh有效时间"ROTATE_REFRESH_TOKENS": False,"BLACKLIST_AFTER_ROTATION": False,"UPDATE_LAST_LOGIN": False,"ALGORITHM": "HS256","SIGNING_KEY": "WoShiLuanXieDe123456", # 加密秘钥"VERIFYING_KEY": "","AUDIENCE": None,"ISSUER": None,"JSON_ENCODER": None,"JWK_URL": None,"LEEWAY": 0,"AUTH_HEADER_TYPES": ("Bearer",), # 请求头用来标记token的字符串"AUTH_HEADER_NAME": "HTTP_AUTHORIZATION","USER_ID_FIELD": "id","USER_ID_CLAIM": "user_id","USER_AUTHENTICATION_RULE": "rest_framework_simplejwt.authentication.default_user_authentication_rule","AUTH_TOKEN_CLASSES": ("rest_framework_simplejwt.tokens.AccessToken",),"TOKEN_TYPE_CLAIM": "token_type","TOKEN_USER_CLASS": "rest_framework_simplejwt.models.TokenUser","JTI_CLAIM": "jti","SLIDING_TOKEN_REFRESH_EXP_CLAIM": "refresh_exp","SLIDING_TOKEN_LIFETIME": timedelta(minutes=5),"SLIDING_TOKEN_REFRESH_LIFETIME": timedelta(days=1),"TOKEN_OBTAIN_SERIALIZER": "rest_framework_simplejwt.serializers.TokenObtainPairSerializer","TOKEN_REFRESH_SERIALIZER": "rest_framework_simplejwt.serializers.TokenRefreshSerializer","TOKEN_VERIFY_SERIALIZER": "rest_framework_simplejwt.serializers.TokenVerifySerializer","TOKEN_BLACKLIST_SERIALIZER": "rest_framework_simplejwt.serializers.TokenBlacklistSerializer","SLIDING_TOKEN_OBTAIN_SERIALIZER": "rest_framework_simplejwt.serializers.TokenObtainSlidingSerializer","SLIDING_TOKEN_REFRESH_SERIALIZER": "rest_framework_simplejwt.serializers.TokenRefreshSlidingSerializer",
}
上述代码中注释部分可自定义,秘钥随便写越复杂越好,不要泄露。
simplejwt包含一个access和一个refresh,一个前端请求想通过后端验证,头部需要包含access,如果access过期且refresh没有过期,则可以通过refresh刷新access。
视图
from rest_framework_simplejwt.views import (TokenObtainPairView,TokenRefreshView,
)urlpatterns = [...path('api/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),...
]
simplejwt写好了两个视图,TokenObtainPairView用来获取access和refresh,TokenRefreshView用来刷新access。
TokenObtainPairView
如图,此视图需要POST方法提交用户名和密码(Django的user模型)。提交后:
返回一个refresh和access:
{"refresh": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoicmVmcmVzaCIsImV4cCI6MTcwNjI1NzA2MiwianRpIjoiNWRiMTRhNWQyZDhkNDBiZWE0N2I4ZjdiNDJlMDk3ZGUiLCJ1c2VyX2lkIjoxfQ.4kc2U33GNTAJVUUBurJWUta8LNbNlB0Ec85H5ZqCsTg","access": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNzA2MTcwOTYyLCJqdGkiOiJjZDg2MDhhNzVlZGY0ZjBlODZiMmVjMWYyZjg2NmFkMCIsInVzZXJfaWQiOjF9.00fjHdR2BcQrcvk6mciof2JdacaXOOx6ba-eaS5wf_w"
}
(如果报str类型无decode的错误,是因为版本问题,可以定位到源码部分将token.decode()改成token)
TokenRefreshView
此视图POST一个refresh,返回一个新的access,将刚生成的refresh提交:
得到一个新的access:
{"access": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNzA2MTcxMjA2LCJqdGkiOiIzOWE4N2YyOGM5ZjQ0ZDE2YmM0ZWU2NmQ4NmM1ZDc3YyIsInVzZXJfaWQiOjF9.qHZzS8xP0x0jGD5vncHEtFaXYRNEjo8NeTwFEAn9FCM"
}
解析JWT
jwt只是一种编码方式,可以直接将上述内容在线解析,Python解析JWT可以通过这种方式:
from base64 import urlsafe_b64decode
from json import loadsdef jwt_decode(access):base64_str = access.split('.')[1]size = len(base64_str) % 4if size == 2:base64_str += '=='elif size == 3:base64_str += '='elif size != 0:raise ValueError('Invalid base64 string')payload = urlsafe_b64decode(base64_str.encode('utf-8')).decode()return loads(payload)
前端解析可以靠jwt-decode库。
使用JWT认证
在需要使用JWT认证的试图类中加上:
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticatedclass IndexView(APIView):permission_classes = ([IsAuthenticated])def get(self, request):players = Player.objects.all()return Response(PlayersModelSerializer(players, many=True).data)
这样再访问index试图时,会提示未提供验证:
需要在请求中添加头信息Authorization:
headers: {'Authorization': 'Bearer ' + token.access,},
这里的Bearer是配置文件中SIMPLE_JWT的AUTH_HEADER_TYPES项,可自定义,但一定要和配置对应。在“Bearer”标识和access之间需要隔开一个空格,即"Bearer "+access,而不是"Bearer"+access。
使用postman测试下,添加头信息:
成功返回结果。postman可以直接添加Authorization,如果提供的是过期的access会返回:
前端根据发送请求使用的工具不同,对应添加头部信息即可,如ajax:
$.ajax({url: 'https://yuming/api/index/',type: 'GET',headers: {'Authorization': 'Bearer ' + token.access,},data: {},async : false,success(resp){console.log(resp);}});