【一】安裝
pip install djangorestframework-simplejwt
【二】配置
# settings.py
INSTALLED_APPS = [
...
'rest_framework', # add
'rest_framework_simplejwt', # add
]
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
),
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_simplejwt.authentication.JWTAuthentication',
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.BasicAuthentication',
),
}
SIMPLE_JWT = {
# token有效時長(返回的 access 有效時長)
'ACCESS_TOKEN_LIFETIME': datetime.timedelta(seconds=30),
# token重新整理的有效時間(返回的 refresh 有效時長)
'REFRESH_TOKEN_LIFETIME': datetime.timedelta(seconds=20),
}
【三】配置路由
# urls.py
urlpatterns = [
# 登入
path('login/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
path('refresh/', TokenRefreshView.as_view(), name='token_refresh'),
path('verify/', TokenVerifyView.as_view(), name='token_verify'),
]
【四】配置認證類
# 匯入這兩個類
from rest_framework_simplejwt.authentication import JWTAuthentication
from rest_framework.permissions import IsAuthenticated
class CarView(ModelViewSet):
serializer_class = CarSerializer
queryset = CarModel.objects.all()
# 認證類和許可權類需要搭配使用
authentication_classes = [JWTAuthentication]
permission_classes = [IsAuthenticated]
【五】內建user表
如果用內建的user表的話,此時直接以 path('login/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
登入就會簽發token,
需要在請求頭加上Authorization,格式:Bearer [token值] 有空格
【六】自定義返回內容
如果需要自定義登入後返回的訊息的話,就需要進行如下配置
【1】序列化類
繼承TokenObtainPairSerializer
重寫validate方法
定製data返回,即為返回訊息
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer
# 序列化類 繼承TokenObtainPairSerialize
class AutoLoginSerializer(TokenObtainPairSerializer):
# 重寫父類的全域性鉤子
def validate(self, attrs):
dic = super().validate(attrs)
data = {
'code': 100,
'message': '登陸成功',
'username': self.user.username,
'refresh': dic.get('refresh'),
'access': dic.get('access')
}
return data
【2】檢視類
如果不是繼承內建user表的話,就需要進行如下配置
# 檢視類
class AutoUserView(GenericViewSet):
serializer_class = AutoLoginSerializer
@action(methods=['POST'], detail=False)
def login(self, request):
serializer = self.get_serializer(data=request.data)
if serializer.is_valid():
return Response(serializer.validated_data)
【3】JWT配置
由於自己定義了序列化類,所以需要在配置項裡面替換預設的序列化類為自己定義的序列化類
# JWT配置 裡面具體配置可以參考文件
SIMPLE_JWT = {
# 用於生成access和刷refresh的序列化器。
"TOKEN_OBTAIN_SERIALIZER": "app02.serializer.AutoLoginSerializer",
}
【七】定製payload內容
自定義序列化類
繼承TokenObtainPairSerializer
重寫get_token,繼承父類的get_token
修改get_tokend的返回值的內容
返回新的值
class AutoLoginSerializer(TokenObtainPairSerializer):
@classmethod
def get_token(cls, user):
token = super().get_token(user)
# 往第二段資料裡面加東西 這裡加一個名字
token['name'] = user.username
return token
# 重寫全域性鉤子
def validate(self, attrs):
dic = super().validate(attrs)
data = {
'code': 100,
'message': '登陸成功',
'username': self.user.username,
'refresh': dic.get('refresh'),
'access': dic.get('access')
}
return data
【八】多方式登入
可以透過多種方式登入,如使用者名稱/手機號/郵箱
【1】檢視類
class UserView(GenericViewSet, CreateModelMixin):
serializer_class = None
def get_serializer_class(self):
if self.action == 'login':
return UserLSerializer
else:
return UserRSerializer
@action(methods=['POST'], detail=False)
def register(self, request):
res = super().create(request)
return Response({'code': 200, 'message': '註冊成功', 'result': res.data})
@action(methods=['POST'], detail=False)
def login(self, request):
serializer = self.get_serializer(data=request.data)
if serializer.is_valid():
refresh = serializer.context.get('refresh')
access = serializer.context.get('access')
return Response({'code': 200, 'message': '登入成功', 'refresh': refresh, 'access': access})
return Response(serializer.errors)
【2】序列化類
class UserLSerializer(serializers.Serializer):
# 因為登入的時候只輸入使用者名稱密碼
# 所以只需要序列化兩個欄位
username = serializers.CharField()
password = serializers.CharField()
# 這個方法用來拿到登入物件
def _get_user(self, attrs):
# 手機號正規表示式
phone_regex = r'^1[3-9]\d{9}$'
# 郵箱正規表示式
email_regex = r'^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$'
# 拿到 手機號/郵箱/使用者名稱
username = attrs.get('username')
# 拿到密碼
password = attrs.get('password')
# 下面分別做判斷 看使用者名稱是以什麼方式登入
if re.match(phone_regex, username):
user = User.objects.filter(mobile=username).first()
elif re.match(email_regex, username):
user = User.objects.filter(email=username).first()
else:
user = User.objects.filter(username=username).first()
if user and user.check_password(password):
return user
def validate(self, attrs):
user = self._get_user(attrs)
# 如果上面驗證失敗user就為None,就拋異常
if not user:
raise ValidationError('使用者名稱或密碼錯誤')
refresh = RefreshToken.for_user(user)
self.context['refresh'] = str(refresh)
self.context['access'] = str(refresh.access_token)
return attrs
【九】自定義使用者表 簽發
如果不使用內建的user表作為使用者表,那麼就需要手動簽發token以及手動認證
【1】檢視類
手動寫登入註冊介面
class NormaUserView(GenericViewSet, CreateModelMixin):
serializer_class = None
def get_serializer_class(self):
if self.action == 'login':
return NormalUserLSerializer
else:
return NormalUserRSerializer
@action(methods=['POST'], detail=False)
def register(self, request):
res = super().create(request)
return Response({'code': 200, 'message': '註冊成功', 'result': res.data})
@action(methods=['POST'], detail=False)
def login(self, request):
serializer = self.get_serializer(data=request.data)
if serializer.is_valid():
refresh = serializer.context.get('refresh')
access = serializer.context.get('access')
return Response({'code': 200, 'message': '登入成功', 'refresh': refresh, 'access': access})
return Response(serializer.errors)
【2】序列化類
手動簽發token
匯入RefreshToken類
呼叫RefreshToken類的for_user方法傳入登入user物件
獲取token,傳入上下文管理器context
class NormalUserLSerializer(serializers.Serializer):
username = serializers.CharField()
password = serializers.CharField()
def _get_user(self, attrs):
# 拿到 手機號/郵箱/使用者名稱
username = attrs.get('username')
# 拿到密碼
password = attrs.get('password')
user = NormalUser.objects.filter(username=username, password=password).first()
print(user)
return user
def validate(self, attrs):
user = self._get_user(attrs)
if not user:
raise ValidationError('使用者名稱或密碼錯誤')
token = RefreshToken.for_user(user)
access = str(token.access_token)
refresh = str(token)
self.context['access'] = access
self.context['refresh'] = refresh
return attrs
【十】自定義使用者表 認證
匯入JWTAuthentication類from rest_framework.exceptions import AuthenticationFailed
寫認證類繼承JWTAuthentication類
重寫authenticate方法
獲取token
呼叫父類get_validated_token以此校驗token
from rest_framework.exceptions import AuthenticationFailed
# 繼承JWTAuthentication
class CommonAuthentication(JWTAuthentication):
# 重寫authenticate
def authenticate(self, request):
# 從請求頭中取出token
token = request.META.get('HTTP_AUTHORIZATION')
# 如果沒有token就說明沒有登入,所以丟擲異常
if not token:
raise AuthenticationFailed('請先登入再操作')
# 呼叫父類get_validated_token以此校驗token,返回值是payload段,包含使用者id
validated_token = self.get_validated_token(token)
# 取到id,從而得到使用者物件
user_id = validated_token.get('user_id')
user = NormalUser.objects.filter(pk=user_id).first()
return user, token