Django自定义用户认证系统Customizingauthentication

王朝学院·作者佚名  2016-08-27
窄屏简体版  字體: 小  |  中  |  大  |  超大  

扩展已有的用户模型Extending the existing User model有两种方法来扩展默认的User Model而不用重写自己的模型。如果你不需要改变存储在数据库中的字段,而只是需要改变Model的行为,您可以创建一个基于User的代理Model。允许的行为包括默认的ordering,custom managers, 或者 custom model methods。

如果你想存储与User有关的信息,可以使用一个OneToOneField字段关联到一个存储额外信息的Model。这一Model通常被称为一个PRofile model模型,它可以用来存储一些非验证所需的信息。例如,你可以创建一个Model:

from django.contrib.auth.models import User

class Employee(models.Model):

user = models.OneToOneField(User)

department = models.CharField(max_length=100)

访问:

>>> u = User.objects.get(username='fsmith')

>>> freds_department = u.employee.department

如果需要将profile model的字段添加到admin管理界面的user页面上,需要在应用app的admin.py中定义InlineModelAdmin

from django.contrib import admin

from django.contrib.auth.admin import UserAdmin

from django.contrib.auth.models import User

from my_user_profile_app.models import Employee

# Define an inline admin descriptor for Employee model

# which acts a bit like a singleton

class EmployeeInline(admin.StackedInline):

model = Employee

can_delete = False

verbose_name_plural = 'employee'

# Define a new User admin

class UserAdmin(UserAdmin):

inlines = (EmployeeInline, )

# Re-register UserAdmin

admin.site.unregister(User)

admin.site.register(User, UserAdmin)

这些profile models并不特别,只是与User Model有一个OneToOne链接。所以当一个user实例创建时,profile model并不会自动创建。

重写User模型Substituting a custom User model有时候User Model并不适合你的网站,比如你要将email而不是username作为认证标识,这时候就需要重写User Model。

首先,需要将settings中的默认User Model覆盖:

AUTH_USER_MODEL = 'myapp.MyUser'

引用Referencing the User model如果AUTH_USER_MODEL已被重设,那当User Model通过ForeignKey或者ManyToManyField访问时,不能直接访问,而是要通过AUTH_USER_MODEL来访问:

from django.conf import settings

from django.db import models

class Article(models.Model):

author = models.ForeignKey(settings.AUTH_USER_MODEL)

指定Specifying a custom User model最简单的定制一个User Model的方法是继承用户类AbstractBaseUser。

源码:

@python_2_unicode_compatible

class AbstractBaseUser(models.Model):

passWord= models.CharField(_('password'), max_length=128)

last_login = models.DateTimeField(_('last login'), blank=True, null=True)

is_active = True

REQUIRED_FIELDS = []

class Meta:

abstract = True

def get_username(self):

"Return the identifying username for this User"

return getattr(self, self.USERNAME_FIELD)

def __str__(self):

return self.get_username()

def natural_key(self):

return (self.get_username(),)

def is_anonymous(self):

"""

Always returns False. This is a way of comparing User objects to

anonymous users.

"""

return False

def is_authenticated(self):

"""

Always return True. This is a way to tell if the user has been

authenticated in templates.

"""

return True

def set_password(self, raw_password):

self.password = make_password(raw_password)

def check_password(self, raw_password):

"""

Returns a boolean of whether the raw_password was correct. Handles

hashing formats behind the scenes.

"""

def setter(raw_password):

self.set_password(raw_password)

self.save(update_fields=["password"])

return check_password(raw_password, self.password, setter)

def set_unusable_password(self):

# Sets a value that will never be a valid hash

self.password = make_password(None)

def has_usable_password(self):

return is_password_usable(self.password)

def get_full_name(self):

raise NotImplementedError('subclasses of AbstractBaseUser must provide a get_full_name() method')

def get_short_name(self):

raise NotImplementedError('subclasses of AbstractBaseUser must provide a get_short_name() method.')

def get_session_auth_hash(self):

"""

Returns an HMAC of the password field.

"""

key_salt = "django.contrib.auth.models.AbstractBaseUser.get_session_auth_hash"

return salted_hmac(key_salt, self.password).hexdigest()

一些关键的字段和方法:

USERNAME_FIELD

必需的。设置认证标识。设置成标识的字段unique必须为True。

class MyUser(AbstractBaseUser):

identifier = models.CharField(max_length=40, unique=True)

...

USERNAME_FIELD = 'identifier'

上面的例子中identifier即作为MyUser的认证标识。

REQUIRED_FIELDS

字段name组成的列表。当创建superuser时用到的字段。

class MyUser(AbstractBaseUser):

...

date_of_birth = models.DateField()

height = models.FloatField()

...

REQUIRED_FIELDS = ['date_of_birth', 'height']

列表中不应该包含USERNAME_FIELD字段和password字段。

is_active

AbstractBaseUser默认为True。

get_full_name()

get_short_name()

AbstractBaseUser的子类必须定义的两个方法。

下面为一些AbstractBaseUser的子类可以使用的方法:

get_username()

返回USERNAME_FIELD的值.

is_anonymous()

返回False。

is_authenticated()

返回True。检查一个user是否已登录。

set_password(raw_password)

设置密码

check_password(raw_password)

检查密码是否正确

set_unusable_password()

设置user无密码

has_usable_password()

Returns False if set_unusable_password() has been called for this user.

get_session_auth_hash()

返回密码字段的HMAC. Used for Session invalidation on password change.

还需要为自己的User Model定义一个custom manager。

class models.CustomUserManager

create_user(*username_field*, password=None, **other_fields)

接受username field和required字段来创建用户。例如,如果使用email作为username field, date_of_birth作为required field:

def create_user(self, email, date_of_birth, password=None):

# create user here

...

create_superuser(*username_field*, password, **other_fields)

创建superuser

def create_superuser(self, email, date_of_birth, password):

# create superuser here

...

create_superuser中的password是必需的。

扩展内置的表单Custom users and the built-in auth formsUserCreationForm

依赖于User Model. 扩展User时必须重写。

UserChangeForm

依赖于User Model. 扩展User时必须重写。

AuthenticationForm

Works with任何AbstractBaseUser子类 ,and will adapt to use the field defined in USERNAME_FIELD.

PasswordResetForm

Assumes that the user model has an integer primary key, has a field named email that can be used to identify the user, and a boolean field named is_active to prevent password resets for inactive users.

SetPasswordForm

Works with 任何AbstractBaseUser子类

PasswordChangeForm

Works with任何AbstractBaseUser子类

AdminPasswordChangeForm

Works with任何AbstractBaseUser子类。

定制admin功能Custom users and Admin如果想自己定义的User Model能与admin管理系统一起使用,还需要定义一些字段和方法。

is_staff

是否允许user访问admin界面

is_active

用户是否活跃。

has_perm(perm, obj=None):

user是否拥有perm权限。

has_module_perms(app_label):

user是否拥有app中的权限

定制用户和权限Custom users and permissions如果要定制User的权限系统,最简单的方法是继承PermissionsMixin

源码:

class PermissionsMixin(models.Model):

"""

A mixin class that adds the fields and methods necessary to support

Django's Group and Permission model using the ModelBackend.

"""

is_superuser = models.BooleanField(_('superuser status'), default=False,

help_text=_('Designates that this user has all permissions without '

'explicitly assigning them.'))

groups = models.ManyToManyField(Group, verbose_name=_('groups'),

blank=True, help_text=_('The groups this user belongs to. A user will '

'get all permissions granted to each of '

'their groups.'),

related_name="user_set", related_query_name="user")

user_permissions = models.ManyToManyField(Permission,

verbose_name=_('user permissions'), blank=True,

help_text=_('Specific permissions for this user.'),

related_name="user_set", related_query_name="user")

class Meta:

abstract = True

def get_group_permissions(self, obj=None):

"""

Returns a list of permission strings that this user has through their

groups. This method queries all available auth backends. If an object

is passed in, only permissions matching this object are returned.

"""

permissions = set()

for backend in auth.get_backends():

if hasattr(backend, "get_group_permissions"):

permissions.update(backend.get_group_permissions(self, obj))

return permissions

def get_all_permissions(self, obj=None):

return _user_get_all_permissions(self, obj)

def has_perm(self, perm, obj=None):

"""

Returns True if the user has the specified permission. This method

queries all available auth backends, but returns immediately if any

backend returns True. Thus, a user who has permission from a single

auth backend is assumed to have permission in general. If an object is

provided, permissions for this specific object are checked.

"""

# Active superusers have all permissions.

if self.is_active and self.is_superuser:

return True

# Otherwise we need to check the backends.

return _user_has_perm(self, perm, obj)

def has_perms(self, perm_list, obj=None):

"""

Returns True if the user has each of the specified permissions. If

object is passed, it checks if the user has all required perms for this

object.

"""

for perm in perm_list:

if not self.has_perm(perm, obj):

return False

return True

def has_module_perms(self, app_label):

"""

Returns True if the user has any permissions in the given app label.

Uses pretty much the same logic as has_perm, above.

"""

# Active superusers have all permissions.

if self.is_active and self.is_superuser:

return True

return _user_has_module_perms(self, app_label)

Django内置的User对象就继承了AbstractBaseUser和PermissionsMixin。

源码:

class AbstractUser(AbstractBaseUser, PermissionsMixin):

"""

An abstract base class implementing a fully featured User model with

admin-compliant permissions.

Username, password and email are required. Other fields are optional.

"""

username = models.CharField(_('username'), max_length=30, unique=True,

help_text=_('Required. 30 characters or fewer. Letters, digits and '

'@/./+/-/_ only.'),

validators=[

validators.RegexValidator(r'^[\w.@+-]+$',

_('Enter a valid username. '

'This value may contain only letters, numbers '

'and @/./+/-/_ characters.'), 'invalid'),

],

error_messages={

'unique': _("A user with that username already exists."),

})

first_name = models.CharField(_('first name'), max_length=30, blank=True)

last_name = models.CharField(_('last name'), max_length=30, blank=True)

email = models.EmailField(_('email address'), blank=True)

is_staff = models.BooleanField(_('staff status'), default=False,

help_text=_('Designates whether the user can log into this admin '

'site.'))

is_active = models.BooleanField(_('active'), default=True,

help_text=_('Designates whether this user should be treated as '

'active. Unselect this instead of deleting accounts.'))

date_joined = models.DateTimeField(_('date joined'), default=timezone.now)

objects = UserManager()

USERNAME_FIELD = 'username'

REQUIRED_FIELDS = ['email']

class Meta:

verbose_name = _('user')

verbose_name_plural = _('users')

abstract = True

def get_full_name(self):

"""

Returns the first_name plus the last_name, with a space in between.

"""

full_name = '%s %s' % (self.first_name, self.last_name)

return full_name.strip()

def get_short_name(self):

"Returns the short name for the user."

return self.first_name

def email_user(self, subject, message, from_email=None, **kwargs):

"""

Sends an email to this User.

"""

send_mail(subject, message, from_email, [self.email], **kwargs)

class User(AbstractUser):

"""

Users within the Django authentication system are represented by this

model.

Username, password and email are required. Other fields are optional.

"""

class Meta(AbstractUser.Meta):

swappable = 'AUTH_USER_MODEL'

现在可以看一个完整的自定义User Model例子:

from django.db import models

from django.contrib.auth.models import (

BaseUserManager, AbstractBaseUser

)

class MyUserManager(BaseUserManager):

def create_user(self, email, date_of_birth, password=None):

"""

Creates and saves a User with the given email, date of

birth and password.

"""

if not email:

raise ValueError('Users must have an email address')

user = self.model(

email=self.normalize_email(email),

date_of_birth=date_of_birth,

)

user.set_password(password)

user.save(using=self._db)

return user

def create_superuser(self, email, date_of_birth, password):

"""

Creates and saves a superuser with the given email, date of

birth and password.

"""

user = self.create_user(email,

password=password,

date_of_birth=date_of_birth

)

user.is_admin = True

user.save(using=self._db)

return user

class MyUser(AbstractBaseUser):

email = models.EmailField(

verbose_name='email address',

max_length=255,

unique=True,

)

date_of_birth = models.DateField()

is_active = models.BooleanField(default=True)

is_admin = models.BooleanField(default=False)

objects = MyUserManager()

USERNAME_FIELD = 'email'

REQUIRED_FIELDS = ['date_of_birth']

def get_full_name(self):

# The user is identified by their email address

return self.email

def get_short_name(self):

# The user is identified by their email address

return self.email

def __str__(self): # __unicode__ on Python 2

return self.email

def has_perm(self, perm, obj=None):

"Does the user have a specific permission?"

# Simplest possible answer: Yes, always

return True

def has_module_perms(self, app_label):

"Does the user have permissions to view the app `app_label`?"

# Simplest possible answer: Yes, always

return True

@property

def is_staff(self):

"Is the user a member of staff?"

# Simplest possible answer: All admins are staff

return self.is_admin

可以看到manager定义了create_user()和create_superuser()方法,MyUser定义了USERNAME_FIELD,REQUIRED_FIELDS字段和get_full_name(),get_short_name()方法,为了能与admin一起使用,还定义了is_active,is_staff,has_perm(),has_module_perms()

要在admin中注册自定义的MyUser,还需要在app的admin.py中重写UserCreationForm和UserChangeForm:

from django import forms

from django.contrib import admin

from django.contrib.auth.models import Group

from django.contrib.auth.admin import UserAdmin

from django.contrib.auth.forms import ReadOnlyPasswordHashField

from customauth.models import MyUser

class UserCreationForm(forms.ModelForm):

"""A form for creating new users. Includes all the required

fields, plus a repeated password."""

password1 = forms.CharField(label='Password', widget=forms.PasswordInput)

password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)

class Meta:

model = MyUser

fields = ('email', 'date_of_birth')

def clean_password2(self):

# Check that the two password entries match

password1 = self.cleaned_data.get("password1")

password2 = self.cleaned_data.get("password2")

if password1 and password2 and password1 != password2:

raise forms.ValidationError("Passwords don't match")

return password2

def save(self, commit=True):

# Save the provided password in hashed format

user = super(UserCreationForm, self).save(commit=False)

user.set_password(self.cleaned_data["password1"])

if commit:

user.save()

return user

class UserChangeForm(forms.ModelForm):

"""A form for updating users. Includes all the fields on

the user, but replaces the password field with admin's

password hash display field.

"""

password = ReadOnlyPasswordHashField()

class Meta:

model = MyUser

fields = ('email', 'password', 'date_of_birth', 'is_active', 'is_admin')

def clean_password(self):

# Regardless of what the user provides, return the initial value.

# This is done here, rather than on the field, because the

# field does not haveaccessto the initial value

return self.initial["password"]

class MyUserAdmin(UserAdmin):

# The forms to add and change user instances

form = UserChangeForm

add_form = UserCreationForm

# The fields to be used in displaying the User model.

# These override the definitions on the base UserAdmin

# that reference specific fields on auth.User.

list_display = ('email', 'date_of_birth', 'is_admin')

list_filter = ('is_admin',)

fieldsets = (

(None, {'fields': ('email', 'password')}),

('Personal info', {'fields': ('date_of_birth',)}),

('Permissions', {'fields': ('is_admin',)}),

)

# add_fieldsets is not a standard ModelAdmin attribute. UserAdmin

# overrides get_fieldsets to use this attribute when creating a user.

add_fieldsets = (

(None, {

'classes': ('wide',),

'fields': ('email', 'date_of_birth', 'password1', 'password2')}

),

)

search_fields = ('email',)

ordering = ('email',)

filter_horizontal = ()

# Now register the new UserAdmin...

admin.site.register(MyUser, MyUserAdmin)

# ... and, since we're not using Django's built-in permissions,

# unregister the Group model from admin.

admin.site.unregister(Group)

最后,别忘了在settings.py中定义AUTH_USER_MODEL:

AUTH_USER_MODEL = 'customauth.MyUser'

 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
 
© 2005- 王朝網路 版權所有 導航