Loading

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
posted @ 2024-05-04 22:08  HuangQiaoqi  阅读(127)  评论(0编辑  收藏  举报