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 Userclass 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 adminfrom django.contrib.auth.admin import UserAdminfrom django.contrib.auth.models import Userfrom my_user_profile_app.models import Employee# Define an inline admin descriptor for Employee model# which acts a bit like a singletonclass EmployeeInline(admin.StackedInline): model = Employee can_delete = False verbose_name_plural = 'employee'# Define a new User adminclass UserAdmin(UserAdmin): inlines = (EmployeeInline, )# Re-register UserAdminadmin.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 settingsfrom django.db import modelsclass 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 modelsfrom 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 userclass 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 formsfrom django.contrib import adminfrom django.contrib.auth.models import Groupfrom django.contrib.auth.admin import UserAdminfrom django.contrib.auth.forms import ReadOnlyPasswordHashFieldfrom customauth.models import MyUserclass 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 userclass 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' |
浙公网安备 33010602011771号