本人也是新学,也参考了很多网上文章,最终实现我的理想结构初步模型,但发现网上并没有类似文章,所以来和大家探讨一下,让新人少走一些弯路。
声明:本文首发于csdn,作者mist2015,目前并没有在其他平台发表,转载请注明。
平台:微信小程序,实现openid作为密码登陆,换取jwt token,并实现用户认证。
所用版本:python3.9+django3.14+rest_framework3.12.2+rest_framework_simplejwt4.6.0
实现:
1、django实现自定义用户结构,关键:AUTH_USER_MODEL
2、管理用户和会员用户体系分离。管理用户是django默认用户体系,可以登陆admin后台。会员用户体系是自定义model,用来保存昵称,openid,爱好等等结构,不包含密码等字段。
首先实现自定义用户模型,创建mauth目录,mauth名字可以自己取,喜欢什么用什么。
python manage.py startapp mauth
然后修改以下文件。
# settings.py文件
INSTALLED_APPS = ['mauth']
AUTH_USER_MODEL = 'mauth.Muser'
# mauth/models.py文件
from django.db import models
from django.contrib.auth.models import (
AbstractBaseUser, PermissionsMixin
)
from django.contrib.auth.models import UserManager as BaseUserManager
from django.utils.deconstruct import deconstructible
class MuserManager(BaseUserManager):
def _create_user(self, username, mobile, password, **extra_fields):
if not username:
raise ValueError('The given username must be set')
username = self.model.normalize_username(username)
user = self.model(username=username, mobile=mobile, **extra_fields)
user.set_password(password)
user.save(using=self._db)
return user
def create_user(self, username, mobile, password=None, **extra_fields):
extra_fields.setdefault('is_staff', False)
extra_fields.setdefault('is_superuser', False)
return self._create_user(username, mobile, password, **extra_fields)
def create_superuser(self, username, mobile, password=None, **extra_fields):
extra_fields.setdefault('is_staff', True)
extra_fields.setdefault('is_superuser', True)
if extra_fields.get('is_staff') is not True:
raise ValueError('Superuser must have is_staff=True.')
if extra_fields.get('is_superuser') is not True:
raise ValueError('Superuser must have is_superuser=True.')
return self._create_user(username, mobile, password, **extra_fields)
class Muser(AbstractBaseUser, PermissionsMixin):
username = models.CharField(verbose_name='用户名', max_length=20, unique=True, help_text='请输入您的名字或昵称!',
validators=[UnicodeUsernameValidator()], error_messages={'unique': ("用户名已存在,请更改!")},
)
mobile = models.CharField(unique=True, max_length=15,
verbose_name="手机号码", help_text='请输入手机号码!',
error_messages={'unique': "该手机号码已注册,请更改或联系管理员!"}
)
openid = models.CharField(verbose_name='微信openID', max_length=100,editable=False ) #, unique=True
is_staff = models.BooleanField(default=False, verbose_name='是否可以访问管理站点')
is_active = models.BooleanField(default=True, verbose_name='是否活动帐户')
# last_login
objects = MuserManager()
USERNAME_FIELD = 'username'
REQUIRED_FIELDS = ['mobile']
class Meta:
verbose_name = "用户"
verbose_name_plural = "用户列表"
def __str__(self):
return self.username
使用AbstractUser可以更简单扩展用户,但不能改变原有模型,邮箱、first name什么的并没有多大意义,于是索性研究了下源码,全部改掉了。本来还自定了一个手机号码验证器,没放上去。
# mauth/admin.py
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from mauth.models import Muser
class UserAdmin(BaseUserAdmin):
fieldsets = (
(None, {'fields': ('username', 'password')}),
#('Personal info', {'fields': ('is_staff',)}),
('Permissions', {'fields': ('is_active', 'is_staff', 'groups', 'user_permissions')}),
)
# 创建用户时显示
add_fieldsets = (
(None, {
'classes': ('wide',),
'fields': ('username', 'mobile', 'password1', 'password2'),
}),
)
list_display = ('username','mobile', 'is_staff', 'is_superuser')
list_filter = ('is_staff', 'is_superuser', 'is_active', 'groups')
search_fields = ('username', 'mobile')
ordering = ('username',)
admin.site.register(Muser, UserAdmin)
当然少不了把mauth/migrations目录删除,数据库删除,重建,再重新使用一下命令重建用户数据库
python manage.py makemigrations mauth
python manage.py migrate
python manage.py createsuperuser
目前创建自定义用户告一段落,可以验证一下试试,唯一缺陷的是admin管理模块中mauth和用户和授权是分开的,没空去研究,有心得的和我分享下。
声明:本文首发于csdn,作者mist2015,目前并没有在其他平台发表,转载请注明。
成功之后,接下来就是做自定义会员模块,这块比较简单,网上文章也比较多,我的也简单发下。
python manage.py startapp mumu
mumu可以自行更改,全部结构就不发了,关键数据结构在此:
另外,这一块可以放在第一步,如果这步都搞不定,说明你数据库设置有问题。
# mumu/models.py
class Member(models.Model):
name = models.CharField(max_length=20, verbose_name="姓名")
openid = models.CharField(max_length=100,blank=True, null=True,editable=False)
声明:本文首发于csdn,作者mist2015,目前并没有在其他平台发表,转载请注明。
最后,实现openid认证,在mauth目录下新建wechat_auth.py文件,其中mauth和Muser更改为你自己的目录和自定义用户名。
# wechat_auth.py
from django.contrib.auth.backends import BaseBackend
#from mauth.models import Muser
from mumu.models import Member as Muser
class OpenIdAuth(BaseBackend):
"""
Authenticates against settings.AUTH_USER_MODEL.
"""
def authenticate(self, request, openid=None, **kwargs):
if openid is None:
return
try:
user = Muser.objects.get(openid=openid)
except Muser.DoesNotExist:
return None
else:
# if user is not None:
# return user
# return None
return user if self.user_can_authenticate(user) else None
def user_can_authenticate(self, user):
"""
Reject users with is_active=False. Custom user models that don't have
that attribute are allowed.
"""
is_active = getattr(user, 'is_active', None)
return is_active or is_active is None
def get_user(self, user_id):
try:
user = Muser.objects.get(pk=user_id)
except Muser.DoesNotExist:
return None
return user if self.user_can_authenticate(user) else None
然后在settings.py设置
# settings.py
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
),
# 全局认证 优先级高于试图类中的配置 login view中,进行用户验证时
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework_simplejwt.authentication.JWTAuthentication',
],
}
AUTHENTICATION_BACKENDS = (
'mauth.wechat_auth.OpenIdAuth',
'django.contrib.auth.backends.ModelBackend',
)
SIMPLE_JWT = {
'ACCESS_TOKEN_LIFETIME': timedelta(minutes=5),
'REFRESH_TOKEN_LIFETIME': timedelta(days=1),
'ALGORITHM': 'HS256',
'AUTH_HEADER_TYPES': ('Bearer',),
'AUTH_HEADER_NAME': 'HTTP_AUTHORIZATION',
}
另外,跨域白名单请自己设置。
然后用postman测试一下,设置post方式,form-data中添加openid:12345,其中12345作为你测试openid,也需要在你后台数据库中设置,返回refresh,access json数据就是正确了。
在此再分享下心得,阅读源码是很重要的,然而我python也是初学,很多源码都没看懂,走了很多弯路,比如(元类,classmethod 修饰符,__getattr__)之类的还是必须要懂的,建议很多和我一样的人还是恶补下基础知识吧。