前言: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 上面看到答案。