一、rest framework simplejwt的安装
pip install djangorestframework-simplejwt
然后在settings.py中添加:
INSTALLED_APPS = [
...
'rest_framework_simplejwt',
...
]
REST_FRAMEWORK = {
...
'DEFAULT_AUTHENTICATION_CLASSES': (
...
'rest_framework_simplejwt.authentication.JWTAuthentication',
)
...
}
在settings.py中添加:
from datetime import timedelta
...
SIMPLE_JWT = {
'ACCESS_TOKEN_LIFETIME': timedelta(minutes=5),
'REFRESH_TOKEN_LIFETIME': timedelta(days=1),
'ROTATE_REFRESH_TOKENS': False,
'BLACKLIST_AFTER_ROTATION': False,
'UPDATE_LAST_LOGIN': False,
'ALGORITHM': 'HS256',
'SIGNING_KEY': SECRET_KEY,
'VERIFYING_KEY': None,
'AUDIENCE': None,
'ISSUER': None,
'JWK_URL': None,
'LEEWAY': 0,
'AUTH_HEADER_TYPES': ('Bearer',),
'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),
}
添加获取jwt和刷新jwt的路由
urlpatterns = [
path('admin/', admin.site.urls),
path('token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
path('token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
]
二、rest framework simplejwt源码
simplejwt引入的两个视图,分别是TokenObtainPairView和TokenRefreshView。
class TokenObtainPairView(TokenViewBase):
_serializer_class = api_settings.TOKEN_OBTAIN_SERIALIZER
token_obtain_pair = TokenObtainPairView.as_view()
TokenObtainPairView和TokenRefreshView的父类都是TokenViewBase,TokenObtainPairView的as_view方法和restframework中介绍的as_view一样,在反射后会调用post方法,因为TokenObtainPairSerializer本身没有post方法,所以会调用基类TokenObtainSerializer的post方法。
def post(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
try:
serializer.is_valid(raise_exception=True)
except TokenError as e:
raise InvalidToken(e.args[0])
return Response(serializer.validated_data, status=status.HTTP_200_OK)
在get_serializer方法中,如果TokenObtainPairView类中_serializer_class等于None,那么get_serializer方法就会将TokenObtainPairSerializer类的对象赋给serializer。
如果需要自定义serializer,那么就只需要在自定义视图类中将自定义的TokenObtainPairSerializer类赋给_serializer_class
在基类的post方法中,serializer是TokenObtainPairSerializer类的对象。在最后会调用TokenObtainPairSerializer的validated_data方法。
class TokenObtainPairSerializer(TokenObtainSerializer):
token_class = RefreshToken
def validate(self, attrs):
data = super().validate(attrs)
refresh = self.get_token(self.user)
data["refresh"] = str(refresh)
data["access"] = str(refresh.access_token)
if api_settings.UPDATE_LAST_LOGIN:
update_last_login(None, self.user)
return data
validate方法又调用了父类的validate方法,下面是父类的validate方法
def validate(self, attrs):
authenticate_kwargs = {
self.username_field: attrs[self.username_field],
"password": attrs["password"],
}
try:
authenticate_kwargs["request"] = self.context["request"]
except KeyError:
pass
self.user = authenticate(**authenticate_kwargs)
if not api_settings.USER_AUTHENTICATION_RULE(self.user):
raise exceptions.AuthenticationFailed(
self.error_messages["no_active_account"],
"no_active_account",
)
return {}
其中的authenticate方法会返回用户名和密码都正确的user对象并赋给self.user。
validate方法是用于验证token的,一共有两个参数,第一个参数是TokenObtainPairSerializer对象
@classmethod
def get_token(cls, user):
return cls.token_class.for_user(user)
其中cls.token_class是rest_framework_simplejwt.tokens.RefreshToken类,因为RefreshToken类没有for_user方法,所以调用了基类Token的for_user方法
@classmethod
def for_user(cls, user):
"""
Returns an authorization token for the given user that will be provided
after authenticating the user's credentials.
"""
user_id = getattr(user, api_settings.USER_ID_FIELD)
if not isinstance(user_id, int):
user_id = str(user_id)
token = cls()
token[api_settings.USER_ID_CLAIM] = user_id
return token
token=cls(),调用rest_framework_simplejwt.tokens.RefreshToken的构造函数,同样因为RefreshToken本身没有__init__方法,所以调用了基类Token的__init__方法。
最后在token[api_settings.USER_ID_CLAIM] = user_id中将user_id放入token中。
def __init__(self, token=None, verify=True):
if self.token_type is None or self.lifetime is None:
raise TokenError(_("Cannot create token with no type or lifetime"))
self.token = token
self.current_time = aware_utcnow()
# Set up token
if token is not None:
# An encoded token was provided
token_backend = self.get_token_backend()
# Decode token
try:
self.payload = token_backend.decode(token, verify=verify)
except TokenBackendError:
raise TokenError(_("Token is invalid or expired"))
if verify:
self.verify()
else:
# 新的Token. 跳过所有验证步骤.
self.payload = {api_settings.TOKEN_TYPE_CLAIM: self.token_type}
# Set "exp" and "iat" claims with default value
self.set_exp(from_time=self.current_time, lifetime=self.lifetime)
self.set_iat(at_time=self.current_time)
# Set "jti" claim
self.set_jti()
在初始化token的过程中,首先初始化token的payload,分别设置payload的token_type、exp、iat和jti。
三、自定义视图
首先需要在api应用中新建文件夹utils,然后新建auth.py,在auth.py中新建视图类MyTokenObtainPairView和MyTokenObtainPairSerializer类
class MyTokenObtainPairView(TokenObtainPairView):
_serializer_class = MyTokenObtainPairSerializer
在类中只有一行,就是设置_serializer_class为新建的MyTokenObtainPairSerializer类。
class MyTokenObtainPairSerializer(TokenObtainPairSerializer):
username_field = 'userID'
def validate(self, attrs):
# data = super().validate(attrs)
password = make_password(attrs["password"])
authenticate_kwargs = {
self.username_field: attrs[self.username_field],
"password": attrs["password"],
}
try:
user = Teachers.objects.get(**authenticate_kwargs)
except Exception as e:
raise exceptions.NotFound(e.args[0])
refresh = self.get_token(user)
data = {"access": str(refresh.access_token), "refresh": str(refresh)}
return data
在MyTokenObtainPairSerializer类中重写了validate方法,该方法会取出用户请求中的username_field和password值,然后获取符合该条件的对象,再利用get_token函数生成新的token