[学习笔记]记录使用JWT实现注册并登录的过程

实现目标:

  1. 通过JWT实现用户登录
  2. 对已登录的用户请求获取可用户信息

忙了一下午,找了视频,看了文档,我还是太笨

理论还是明白一点,但就是想不明白,怎么实现的.

在我很纠结的时候,我果断列出想不明白的地方:

  1. 不知道用户是不是登录状态
  2. 不知道后端怎么获得当前登录用户信息

最近在学习DRF,然后手上有个项目,所以干脆直接拿小项目来练手

先罗列下,用户操作流程

注册并登录流程:

   1. 用户输入手机号码,点击请求验证码
 2. 用户输入验证码,后端验证成功,数据库新增此用户
 3. 使用django-JWT下放`token`

获取用户信息流程:

  1. 验证用户是否处于登录状态,
  2. 登录的情况下,取出该用户的信息

注册并登录

新建用户

视图类

class UserViewSet(mixins.CreateModelMixin, viewsets.GenericViewSet):
    
    # 指定序列化类
    serializer_class = UserRegSerializer
    # 包含所有用户的查询集
    queryset = User.objects.all()

    def create(self, request, *args, **kwargs):
        # 创建用户
        # 获取序列化对象
        serializer = self.get_serializer(data=request.data)
        # 验证序列化数据的有效性
        serializer.is_valid(raise_exception=True)
        # 完成上面一步就代表,序列化类里的数据是没有异常的,可以继续放心创建新用户
        
        # 创建新用户
        user = self.perform_create(serializer)
        
        # return Response(re_dict, status=status.HTTP_201_CREATED, headers=headers)

    def get_object(self):
        return self.request.user

    def perform_create(self, serializer):
        return serializer.save()

上面的视图类,是不完整的,先来一点一点理清思路

  1. 通过序列化类,证实了从前端接收的数据,是有效的
  2. 在数据有效的基础上,perform_create执行创建函数,新建用户

再贴下UserRegSerializer序列化类的代码

class UserRegSerializer(serializers.ModelSerializer):
    """用户手机号码注册序列化器"""
    code = serializers.CharField(required=True, max_length=6, min_length=6, write_only=True,
                                 error_messages={
                                     'blank': '验证码不能为空',
                                     'required': '请输入验证码',
                                     'max_length': '验证码长度错误',
                                     'min_length': '验证码长度错误',
                                 },
                                 help_text='验证码')
    username = serializers.CharField(required=True, min_length=11, max_length=11,
                                     error_messages={
                                         'blank': '手机号码不能为空',
                                         'required': '手机号码不能为空',
                                         'max_length': '验证码长度错误',
                                         'min_length': '验证码长度错误',
                                     })

    class Meta:
        model = User
        fields = ['username', 'code']

    # 在序列化器内重写create方法
    def create(self, validated_data):
        return User.objects.create_user(username=validated_data['username'], tel=validated_data['username'],cookie=validated_data['cookie'])

    def validated_username(self, username):
        """验证手机是否有效"""
        if not re.match(settings.REGEX_MOBILE, username):
            raise serializers.ValidationError("手机号码不合法")
        return username

    def validate(self, attrs):
        """验证登录"""
        if not User.objects.filter(username=attrs['username']).exists():
            # 用户不存在,需要去验证是否登录成功
            # check_verify_code是我自己写的用户验证
            res = User.check_verify_code(self, attrs['username'], attrs['code'])
            if res['status']:
                attrs['cookie'] = res['cookies']
            else:
                raise serializers.ValidationError("登录失败,请稍后再试")
        del attrs['code']
        return attrs

这个序列化类,接收了两个参数usernamecode

username:手机号码

code:就是手机验证码

validated_username: 通过正则表达式,校验手机号码是不是合法的

create: 重写了create方法,根据自己的需求新建用户

validate: 验证数据,最后返回数据

回到视图类中,视图类中的perform_create方法,就是去执行了序列化类中的我们重写的create方法

至此,这里就创建了新用户

    def perform_create(self, serializer):
        return serializer.save()

返回消息

创建了新用户,创建成功与否,还应当将信息返回

在网站上,新建用户之后一般会有两种操作:

  1. 用户注册成功,跳转到登录页面,用户自行登录
  2. 用户注册成功,直接给此用户token,表示用户处于登录状态,无需用户再次手动登录

这里,我选择的第二种.用户注册成功之后,我应当使用JWT创建新的token,返回给前端

    def create(self, request, *args, **kwargs):
        # 创建用户
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        user = self.perform_create(serializer)
        
        # 将序列化之后的数据,复制给re_dict
        re_dict = serializer.data
        # 用户信息编码并 赋值给payload
        payload = jwt_payload_handler(user)
        # 将payload 编码成token
        re_dict["token"] = jwt_encode_handler(payload)
        # 返回用户名
        re_dict["username"] = user.username
        headers = self.get_success_headers(serializer.data)
        return Response(re_dict, status=status.HTTP_201_CREATED, headers=headers)

如此以来,既创建了用户,又通过JWT使得用户处于了登录状态

前端接收到token之后,再进行相应的处理即可

UTOOLS1590454061487.png

用户信息获取

获取用户信息,我们可以单独再写一个类,但是这样不太友好

可以在UserViewSet的基础上新增获取用户信息的方法

一个视图类,只能指定一个序列化器,而此时我们既要完成注册并登录还要获取用户信息的功能

我们可以根据请求方法的不同作出改变.

重写get_serializer_classget_permissions方法

    def get_serializer_class(self):
        """根据条件使用对应的序列化器"""
        if self.action == "retrieve":
            # 获取一用户实例
            return UserDetailSerializer
        elif self.action == "create":
            # 新建用户
            return UserRegSerializer
        return UserDetailSerializer

    def get_permissions(self):
        if self.action == "retrieve":
            return [permission() for permission in self.permission_classes]
        elif self.action == "create":
            return []
        return []

get_serializer_class根据函数名,也能明白是什么意思,获取序列化类

重写这个函数,在当中先对action进行判断,如果是retrieve那么就是获取信息,则将UserDetailSerializer用户信息的序列化类,如果是create则就是新建用户,则返回UserRegSerializer用户注册的序列化类

再解释get_permissions之前,先来理一下思路

如果是注册登录,那么此时用户是游客状态,请求注册接口,是没有权限限制的

而获取当前用户的信息,是必须需要这个用户处于登录状态.

那么这里的问题是: 无登录状态下完成注册,登录状态下获取用户信息

新增permission_classesauthentication_classes, 用于身份验证

# ...
from rest_framework.authentication import SessionAuthentication
from rest_framework.permissions import IsAuthenticated
from rest_framework_jwt.authentication import JSONWebTokenAuthentication


class UserViewSet(mixins.CreateModelMixin, viewsets.GenericViewSet, mixins.RetrieveModelMixin):

    serializer_class = UserRegSerializer
    queryset = User.objects.all()
    # 用户登录的情况下,才能继续下面的操作
    permission_classes = [IsAuthenticated]

    authentication_classes = [JSONWebTokenAuthentication, SessionAuthentication]
    
    # ...

重写get_permissions方法,根据实际情况,判断是否需要用户认证

    def get_permissions(self):
        if self.action == "retrieve":
            return [permission() for permission in self.permission_classes]
        elif self.action == "create":
            return []
        return []

带着token按规定格式,请求用户信息接口 [成功]

默认格式: Authorization: JWT token

深度截图_选择区域_20200526084628.png

添加新评论