用 Django 实现用户自定义模块,自定义认证,openid登陆换取jwt。

本人也是新学,也参考了很多网上文章,最终实现我的理想结构初步模型,但发现网上并没有类似文章,所以来和大家探讨一下,让新人少走一些弯路。

声明:本文首发于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__)之类的还是必须要懂的,建议很多和我一样的人还是恶补下基础知识吧。

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值