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' |