django 自定义用户认证

前言:Django 自带的用户认证系统已经可以满足大部分的情况,但是有时候我们有某些特定的需求,比如把唯一标识改成email,或加入用户组等等,自定义用户认证可以根据项目的需求定制化和扩展认证系统。Django 的确是强大无比的,支持使用其他的认证系统、也可以扩展Django的User模块,还可以完全自定义新的认证模块。

官方文档: https://docs.djangoproject.com/en/1.11/topics/auth/customizing/

自定义用户 models

使用Django自定义用户模型必须满足:

模型必须有一个唯一的字段,可用于识别目的。
用户给定名称为“短”的标识,用户的全名为“长”标识符。他们可以返回完全相同的值。

构建一个符合用户自定义模型的最简单的方法是继承abstractbaseuser类。abstractbaseuser提供一个用户模型的核心实现,包括密码和符号密码重置。Django自带用用户认证User也是继承了它。一些关键的实现细节:

  • USERNAME_FIELD
    必须有一个唯一标识--USERNAME_FIELD
class MyUser(AbstractBaseUser):
    identifier = models.CharField(max_length=40, unique=True)
    ...
    USERNAME_FIELD = 'identifier'
  • REQUIRED_FIELDS
    创建superuser时的必须字段
class MyUser(AbstractBaseUser):
    ...
    date_of_birth = models.DateField()
    height = models.FloatField()
    ...
    REQUIRED_FIELDS = ['date_of_birth', 'height']

django 1.11 新增了EMAIL_FIELD,

  • abstractbaseuser提供的方法
    is_active(),is_authenticated()......

具体实现

# -*- coding: utf-8 -*-
# author: itimor
from django.db import models
from django.contrib.auth.models import BaseUserManager, AbstractBaseUser
class CmsUserManager(BaseUserManager):
    def create_user(self, username, email, password=None):
        '''username 是唯一标识,没有会报错'''
        if not username:
            raise ValueError('Users must have an username')
        user = self.model(
            username=username,
            email=email,
        )
        user.set_password(password)  # 检测密码合理性
        user.save(using=self._db)  # 保存密码
        return user
    def create_superuser(self, username, email, password):
        user = self.create_user(username=username,
                                email=email,
                                password=password,
                                )
        user.is_admin = True  # 比创建用户多的一个字段
        user.save(using=self._db)
        return user
class CmsUser(AbstractBaseUser):
    username = models.CharField(max_length=32, unique=True, db_index=True)
    email = models.EmailField(max_length=255, unique=True, blank=True)
    name = models.CharField(max_length=100, verbose_name='中文名')
    head_img = models.ImageField(blank=True, upload_to="uploads/portrait", verbose_name='头像')
    group = models.ManyToManyField('CmsGroup', null=True, blank=True, verbose_name='部门或组')
    create_date = models.DateField(auto_now=True, verbose_name='创建时间')
    is_active = models.BooleanField(default=True)
    is_admin = models.BooleanField(default=False)
    USERNAME_FIELD = 'username'  # 必须有一个唯一标识--USERNAME_FIELD
    EMAIL_FIELD = 'email'
    REQUIRED_FIELDS = ['email']  # 创建superuser时的必须字段
    def get_full_name(self):
        return self.name
    def get_short_name(self):
        return self.username
    '''django自带后台权限控制,对哪些表有查看权限等'''
    def has_perm(self, perm, obj=None):
        return True
    '''用户是否有权限看到app'''
    def has_module_perms(self, app_label):
        return True
    def __str__(self):  # __unicode__ on Python 2
        return self.username
    @property
    def is_staff(self):
        return self.is_admin
    class Meta:
        verbose_name = '用户'
        verbose_name_plural = '用户'
        permissions = (
            ("view_users", "Can see available userlist"),
        )
    objects = CmsUserManager()  # 创建用户
class CmsGroup(models.Model):
    name = models.CharField(max_length=64, verbose_name='部门')
    owner = models.ForeignKey('CmsUser', default='admin', verbose_name='负责人')
    remarks = models.CharField(max_length=64, blank=True, verbose_name='备注')
    def __str__(self):
        return self.name
    class Meta:
        verbose_name = '组'
        verbose_name_plural = '部门'

修改全局设置

setting.py

AUTH_USER_MODEL = "users.CmsUser"

现在初始化数据库,登录后台。此时密码是明文显示,而且不能重置其他用户密码,这个时候我们还有自定义 admin显示

自定义用户 admin

# -*- coding: utf-8 -*-
# author: itimor
from django.contrib import admin
from users.models import CmsUser, CmsGroup
from django import forms
from django.contrib.auth.models import Group
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from django.contrib.auth.forms import ReadOnlyPasswordHashField
class UserCreationForm(forms.ModelForm):
    error_messages = {
        'password_mismatch': ("The two password fields didn't match."),
    }
    password1 = forms.CharField(label='密码', widget=forms.PasswordInput)
    password2 = forms.CharField(label='重复密码', widget=forms.PasswordInput)
    class Meta:
        model = CmsUser
        fields = ('username',)
    def clean_password2(self):
        password1 = self.cleaned_data.get("password1")
        password2 = self.cleaned_data.get("password2")
        if password1 and password2 and password1 != password2:
            raise forms.ValidationError(
                self.error_messages['password_mismatch'],
                code='password_mismatch',
            )
        return password2
    def save(self, commit=True):
        user = super(UserCreationForm, self).save(commit=False)
        user.set_password(self.cleaned_data["password1"])
        if commit:
            user.save()
        return user
class UserChangeForm(forms.ModelForm):
    password = ReadOnlyPasswordHashField(label=("Password"),
                                         help_text=("你需要重新设置密码,不要犹豫了,老司机,<a href=\"../password/\">快上车</a>."))
    class Meta:
        model = CmsUser
        fields = '__all__'
    def clean_password(self):
        return self.initial["password"]
class GroupAdmin(admin.ModelAdmin):
    list_display = ('name', 'owner', 'remarks')
class UserAdmin(BaseUserAdmin):
    form = UserChangeForm
    add_form = UserCreationForm
    list_display = ('username', 'name', 'email', 'is_admin')
    list_filter = ('is_admin',)
    fieldsets = (
        ('Primary info', {'fields': ('username', 'password')}),
        ('Personal info', {'fields': ('email', 'name', 'group', 'head_img')}),
        ('Permissions', {'fields': ('is_admin', 'is_active')}),
    )
    add_fieldsets = (
        ('Add user', {
            'classes': ('wide',),
            'fields': ('username', 'email', 'name', 'password1', 'password2', 'group', 'is_admin', 'is_active')}
         ),
    )
    search_fields = ('username',)
    ordering = ('username',)
    filter_horizontal = ('group',)
admin.site.register(CmsUser, UserAdmin)
admin.site.register(CmsGroup, GroupAdmin)
admin.site.unregister(Group)

此时我们在登录后台查看。

效果查看

添加新用户

添加新用户

ps: 我给django admin换成 django-suit@v2,所以看这不一样,大家有兴趣也可以换成这个;

用户列表

用户列表

重置密码

点击 上车

重置密码

自此,自定义用户认证完成。

  • 遇到一个坑点
    重置密码那块,只要一点击就会404,
    password = ReadOnlyPasswordHashField(label=("Password"),
        help_text=("你需要重新设置密码,不要犹豫了,老司机,<a href=\"/password/\">快上车</a>."))

好多网上文档都一样,如果用上面的,点击重置时会跳到 id/change/password,找不到这个地址。
而 django 1.9+之后的 改成

    password = ReadOnlyPasswordHashField(label=("Password"),
        help_text=("你需要重新设置密码,不要犹豫了,老司机,<a href=\"../password/\">快上车</a>."))

链接是 id/password,这个才能正确的修改密码,找了好久终于在 stackoverflow 上面看到答案。

参考文档:http://www.cnblogs.com/daliangtou/p/5435385.html

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 144,247评论 1 305
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 61,830评论 1 258
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 95,531评论 0 214
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 41,345评论 0 183
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 49,160评论 1 260
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 38,936评论 1 178
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 30,538评论 2 275
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 29,291评论 0 168
  • 想象着我的养父在大火中拼命挣扎,窒息,最后皮肤化为焦炭。我心中就已经是抑制不住地欢快,这就叫做以其人之道,还治其人...
    爱写小说的胖达阅读 29,162评论 6 237
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 32,654评论 0 214
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 29,401评论 2 217
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 30,747评论 1 232
  • 白月光回国,霸总把我这个替身辞退。还一脸阴沉的警告我。[不要出现在思思面前, 不然我有一百种方法让你生不如死。]我...
    爱写小说的胖达阅读 24,297评论 1 33
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 27,206评论 2 213
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 31,670评论 3 213
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,661评论 0 9
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,089评论 0 169
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 33,677评论 2 233
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 33,819评论 2 237

推荐阅读更多精彩内容

  • 官方文档:https://docs.djangoproject.com/en/1.10/topics/auth/c...
    SateZheng阅读 2,068评论 1 4
  • cms_project 项目,自定义用户和相关权限设置 1.重写用户模型 1.1 修改配置文件,覆盖默认的User...
    常大鹏阅读 25,593评论 1 26
  • django——重写用户模型 Django内建的User模型可能不适合某些类型的项目。例如,在某些网站上使用邮件地...
    常大鹏阅读 26,062评论 2 29
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,007评论 18 139
  • 4 创建一个社交网站 在上一章中,你学习了如何创建站点地图和订阅,并且为博客应用构建了一个搜索引擎。在这一章中,你...
    lakerszhy阅读 2,082评论 0 7