前言:Django 自帶的用戶認證系統(tǒng)已經(jīng)可以滿足大部分的情況,但是有時候我們有某些特定的需求,比如把唯一標識改成email,或加入用戶組等等挪挤,自定義用戶認證可以根據(jù)項目的需求定制化和擴展認證系統(tǒng)。Django 的確是強大無比的关翎,支持使用其他的認證系統(tǒng)扛门、也可以擴展Django的User模塊,還可以完全自定義新的認證模塊纵寝。
官方文檔: https://docs.djangoproject.com/en/1.11/topics/auth/customizing/
自定義用戶 models
使用Django自定義用戶模型必須滿足:
模型必須有一個唯一的字段论寨,可用于識別目的。
用戶給定名稱為“短”的標識爽茴,用戶的全名為“長”標識符葬凳。他們可以返回完全相同的值。
構(gòu)建一個符合用戶自定義模型的最簡單的方法是繼承abstractbaseuser類室奏。abstractbaseuser提供一個用戶模型的核心實現(xiàn)火焰,包括密碼和符號密碼重置。Django自帶用用戶認證User也是繼承了它胧沫。一些關(guān)鍵的實現(xiàn)細節(jié):
- USERNAME_FIELD
必須有一個唯一標識--USERNAME_FIELD
class MyUser(AbstractBaseUser):
identifier = models.CharField(max_length=40, unique=True)
...
USERNAME_FIELD = 'identifier'
- REQUIRED_FIELDS
創(chuàng)建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()......
具體實現(xiàn)
# -*- 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 # 比創(chuàng)建用戶多的一個字段
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='創(chuàng)建時間')
is_active = models.BooleanField(default=True)
is_admin = models.BooleanField(default=False)
USERNAME_FIELD = 'username' # 必須有一個唯一標識--USERNAME_FIELD
EMAIL_FIELD = 'email'
REQUIRED_FIELDS = ['email'] # 創(chuàng)建superuser時的必須字段
def get_full_name(self):
return self.name
def get_short_name(self):
return self.username
'''django自帶后臺權(quán)限控制占业,對哪些表有查看權(quán)限等'''
def has_perm(self, perm, obj=None):
return True
'''用戶是否有權(quán)限看到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() # 創(chuàng)建用戶
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"
現(xiàn)在初始化數(shù)據(jù)庫,登錄后臺纯赎。此時密碼是明文顯示谦疾,而且不能重置其他用戶密碼,這個時候我們還有自定義 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>."))
好多網(wǎng)上文檔都一樣,如果用上面的必搞,點擊重置時會跳到 id/change/password
必指,找不到這個地址。
而 django 1.9+之后的 改成
password = ReadOnlyPasswordHashField(label=("Password"),
help_text=("你需要重新設置密碼恕洲,不要猶豫了塔橡,老司機,<a href=\"../password/\">快上車</a>."))
鏈接是 id/password
霜第,這個才能正確的修改密碼葛家,找了好久終于在 stackoverflow 上面看到答案。