simple-jwt的简单使用
【一】安装
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