Django uses DRF + Simple JWT to complete the registration, login and authentication of small programs using custom users

Django uses DRF + Simple JWT to complete the registration, login and authentication of small programs using custom users

Under the premise that djangorestframework-simplejwt has been configured

Model classes and serializers

Applet user model class

The model class here does not inherit the user model class that comes with django. The advantage is that users of the applet later cannot access the admin side. The disadvantage is that it may affect the permission management that comes with django. If there are only users on the applet side, there is no problem, but if there are other users, there may be problems, because there is no password in this model, as long as you get the user's code generated by the applet, you can get the user's token through the login interface (small program The code on the terminal is different each time it is generated, and the validity period should be 5 minutes)

class User(models.Model):

    USER_TYPE = ((1, '顾客'),
                 (2, '商家'))

    GENDER = ((0, '男'),
              (1, '女'),
              (2, '未知'))

    username = models.CharField('用户名', max_length=20, blank=True)
    tel = models.BigIntegerField('手机号', unique=True, blank=True, null=True)
    openid = models.CharField('小程序openid', unique=True, max_length=100, blank=True, null=True)
    avatar_url = models.URLField('头像', help_text='头像', null=True, blank=True)
    unionid = models.CharField('小程序unionid', unique=True, max_length=100, blank=True, null=True)
    nickname = models.CharField('微信昵称', max_length=100, blank=True, null=True)
    gender = models.IntegerField('性别', choices=GENDER, default=2)
    type = models.IntegerField('用户类型', choices=USER_TYPE, default=1)
  # 用自定义的用户需要重写此方法
    @property
    def is_authenticated(self):
        """
        Always return True. This is a way to tell if the user has been
        authenticated in templates.
        """
        return True

    def __str__(self):
        return self.openid

    class Meta:
        db_table = 'user'
        verbose_name = '用户管理'
        verbose_name_plural = verbose_name

Applet User Serializer

All the information in the user table of the applet, of course, can be added to the model class by yourself, such as registration time or update time, last login time, etc. This is just a demonstration, not added


class UserSerializer(serializers.ModelSerializer):
    """用户序列化器"""

    class Meta:
        model = User
        fields = '__all__'

Custom Authentication Class

It is mainly used to obtain users, and other methods inherit the methods in JWTAuthentication of simplejwt.
Create a new file Authentication.py directly in the user app directory and write the following content

from rest_framework_simplejwt.authentication import JWTAuthentication
from rest_framework_simplejwt.exceptions import InvalidToken, AuthenticationFailed

from apps.user.models import User

# 自定义的解析token的方法 下面会用到
from utils.token_get_user import get_user

class MyJWTAuthentication(JWTAuthentication):
    """
    继承JWTAuthentication类, 返回自定义User对象
    """
	# get_user用于返回用户,重写后在其它地方使用request.user时可以直接得到自定义的小程序用户
    def get_user(self, validated_token):
        try:
            user_id = get_user_id(str(validated_token))  # 此处为自定义的解析token方法, 可以解码token得到其中的信息,重点是拿到user_id 用于后续的获取用户
        except KeyError:
            raise InvalidToken(_('Token不包含可识别的用户标识'))

        try:
            user = User.objects.get(**{'id': user_id})
        except User.DoesNotExist:
            raise AuthenticationFailed(_('未找到用户'), code='user_not_found')

        return user


get_user_id method: used to parse our token to get user_id

import jwt
import time

# ConvLife为我的项目名称,此处导入的是项目的settings,主要是拿到自定义的secret_key
from ConvLife import settings


def get_user_id(t):
    """根据token得到当前用户user_id"""
    try:
        decode_data = jwt.decode(t, secret_key=settings.SECRET_KEY, verify=False, algorithms=['HS256'])
        print(decode_data)
        if int(decode_data['exp']) < int(time.time()):
            return "token过期"
        return decode_data['user_id']
    except Exception as e:
        return "token错误:\n"+str(e)

Mini program login and manually issue token

user view

When logging in, this is actually rewriting create. The idea is roughly: obtain the openid through the code passed in by the user (if the openid is not obtained, it means that the code is wrong or the user has not entered the incoming code), and then use the openid to find the user in the user table. If it does not exist, create a new one, if it exists, read it, then generate a token for the read or newly created user, and return it to the front end

# get_wx_openid 是调用微信开放接口,使用小程序传到后端的code去请求openid,openid作为唯一标识
from utils.wx import get_wx_openid

from utils.Authentication import MyJWTAuthentication


class WxLogin(mixins.CreateModelMixin, viewsets.GenericViewSet):
    queryset = User.objects.all()
    serializer_class = UserSerializer

    def create(self, request, *args, **kwargs):
		# code是小程序端可以直接获取到的,获取到后和userinfo一起Post到后端 
        code = request.data.get('code', '')
        
        # 通过传入的code来获取openid
        openid_res = get_wx_openid(code)
        try:
            openid = openid_res['openid'] or ''
        except KeyError as e:
            return Response({'message': '微信调用失败'}, status=status.HTTP_503_SERVICE_UNAVAILABLE)

        # 尝试查找用户,如果用户存在则读取用户,用户不存在则新建用户
        try:
            user = User.objects.get(openid=openid)
        except User.DoesNotExist:
        	# userinfo 是小程序端可以直接获取到的,获取到后和code 一起Post到后端 
            user_info = request.data.get('userInfo')
            print(user_info)
            user = User.objects.create(openid=openid,
                                       avatar_url=user_info['avatarUrl'],
                                       nickname=str(user_info['nickName']),
                                       gender=user_info['gender'])
            user.save()

        # 手动签发jwt token
        refresh = RefreshToken.for_user(user)
        resp_data = {
            'user_id': user.id,
            "refresh": str(refresh),
            "access": str(refresh.access_token)
        }
        return Response(resp_data)

routing

Here, the front end is directly configured to send the code and userinfo to request. If you need to test, you must first obtain the code from the WeChat applet. You must bring the code to the request, otherwise an error will be reported!

from .views import WxLogin

router = routers.SimpleRouter()

# 小程序用户
router.register(r'login/', WxLogin, basename="login")

urlpatterns = router.urls

Failed to get token with wrong code
Feel free to request code

Use the correct code to get the token successfully
Correct code request, the request is successful

use

permission_classes = [permissions.IsAuthenticated]Add and to the view that requires the login verification of the applet user authentication_classes = (MyJWTAuthentication,). When obtaining user favorites or favorites, the user will need to be a logged-in user and will use our custom class. Do not add it to the logged-in view class, only when verification is required Added to the view class.

For example:

from rest_framework import mixins
from rest_framework.permissions import IsAuthenticated

from .models import UserFav
from .serializers import UserFavSerializer

from utils.Authentication import MyJWTAuthentication


class UserFavViewSet(mixins.CreateModelMixin, mixins.DestroyModelMixin, mixins.ListModelMixin, viewsets.GenericViewSet):
    """
    create: 用户收藏藏品
    取消收藏商品

    list: 获取收藏藏品列表
    """
    queryset = UserFav.objects.all()
    serializer_class = UserFavSerializer
	
	permission_classes = [IsAuthenticated]  # 增加此行
    authentication_classes = [MyJWTAuthentication, ]   # 增加此行

    filter_backends = [DjangoFilterBackend, filters.SearchFilter]
    search_fields = ('goods__good_name', 'goods__goods_brief')
    
    # 获取到当前用户的收藏列表
    def get_queryset(self):
        return UserFav.objects.filter(user=self.request.user.id)


A novice blogger, I mainly want to record the problems I encountered in my study and the process of solving them. There are many deficiencies. Please forgive me if I don’t understand something. You can also private message me. I will reply as soon as possible after seeing it.

Guess you like

Origin blog.csdn.net/aifengaopeng/article/details/126916466