首
前后端分离的项目更多使用JWT认证------Json Web Token。本文记录djangorestframework-simplejwt的使用方式。文档
安装
pip install djangorestframework-simplejwt
python
INSTALLED_APPS = [
'rest_framework_simplejwt',
]
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_simplejwt.authentication.JWTAuthentication',
)
}
from datetime import timedelta
SIMPLE_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。
视图
python
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:
python
{
"refresh": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoicmVmcmVzaCIsImV4cCI6MTcwNjI1NzA2MiwianRpIjoiNWRiMTRhNWQyZDhkNDBiZWE0N2I4ZjdiNDJlMDk3ZGUiLCJ1c2VyX2lkIjoxfQ.4kc2U33GNTAJVUUBurJWUta8LNbNlB0Ec85H5ZqCsTg",
"access": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNzA2MTcwOTYyLCJqdGkiOiJjZDg2MDhhNzVlZGY0ZjBlODZiMmVjMWYyZjg2NmFkMCIsInVzZXJfaWQiOjF9.00fjHdR2BcQrcvk6mciof2JdacaXOOx6ba-eaS5wf_w"
}
(如果报str类型无decode的错误,是因为版本问题,可以定位到源码部分将token.decode()改成token)
TokenRefreshView
此视图POST一个refresh,返回一个新的access,将刚生成的refresh提交:
得到一个新的access:
python
{
"access": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNzA2MTcxMjA2LCJqdGkiOiIzOWE4N2YyOGM5ZjQ0ZDE2YmM0ZWU2NmQ4NmM1ZDc3YyIsInVzZXJfaWQiOjF9.qHZzS8xP0x0jGD5vncHEtFaXYRNEjo8NeTwFEAn9FCM"
}
解析JWT
jwt只是一种编码方式,可以直接将上述内容在线解析,Python解析JWT可以通过这种方式:
python
from base64 import urlsafe_b64decode
from json import loads
def jwt_decode(access):
base64_str = access.split('.')[1]
size = len(base64_str) % 4
if 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认证的试图类中加上:
python
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated
class IndexView(APIView):
permission_classes = ([IsAuthenticated])
def get(self, request):
players = Player.objects.all()
return Response(PlayersModelSerializer(players, many=True).data)
这样再访问index试图时,会提示未提供验证:
需要在请求中添加头信息Authorization:
python
headers: {
'Authorization': 'Bearer ' + token.access,
},
这里的Bearer是配置文件中SIMPLE_JWT的AUTH_HEADER_TYPES项,可自定义,但一定要和配置对应。在"Bearer"标识和access之间需要隔开一个空格,即"Bearer "+access,而不是"Bearer"+access。
使用postman测试下,添加头信息:
成功返回结果。postman可以直接添加Authorization,如果提供的是过期的access会返回:
前端根据发送请求使用的工具不同,对应添加头部信息即可,如ajax:
javascript
$.ajax({
url: 'https://yuming/api/index/',
type: 'GET',
headers: {
'Authorization': 'Bearer ' + token.access,
},
data: {
},
async : false,
success(resp){
console.log(resp);
}
});