Django用户认证(四)自定义认证Customizing authentication

原文:https://www.cnblogs.com/linxiyue/p/4061044.html

扩展已有的用户模型Extending the existing User model

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

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

1
2
3
4
5
from django.contrib.auth.models import User
 
class Employee(models.Model):
    user = models.OneToOneField(User)
    department = models.CharField(max_length=100)

访问:

1
2
>>> u = User.objects.get(username='fsmith')
>>> freds_department = u.employee.department

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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覆盖:

1
AUTH_USER_MODEL = 'myapp.MyUser'

引用Referencing the User model

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

1
2
3
4
5
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。

源码:

一些关键的字段和方法:

USERNAME_FIELD

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

1
2
3
4
class MyUser(AbstractBaseUser):
    identifier = models.CharField(max_length=40, unique=True)
    ...
    USERNAME_FIELD = 'identifier'

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

REQUIRED_FIELDS

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

1
2
3
4
5
6
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:

1
2
3
def create_user(self, email, date_of_birth, password=None):
    # create user here
    ...

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

创建superuser

1
2
3
def create_superuser(self, email, date_of_birth, password):
    # create superuser here
    ...

create_superuser中的password是必需的。

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

UserCreationForm

依赖于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

源码:

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

源码:

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
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 have access to 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:

1
AUTH_USER_MODEL = 'customauth.MyUser'

  

posted @ 2019-02-12 15:29  chdltanke  阅读(259)  评论(0编辑  收藏  举报