Table of contents
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
Use the correct code to get the token successfully
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.