在Django中自定义身份验证¶
Django提供的身份验证对于大多数常见情况来说已经足够了,但是您可能需要通过开箱即用的默认值来满足需求。要为您的项目自定义认证,需要了解所提供系统的哪些方面是可扩展或可替换的。本文档提供了有关如何自定义认证系统的详细信息。
当用户模型存储的用户名和密码需要根据与Django的默认值不同的服务进行身份验证时,身份验证后端 提供了一个可扩展系统。
你可以给你的模型 自定义权限,可以通过Django的授权系统检查。
您可以将 延伸 的默认 User
模型,或 替代 一个完全自定义的模型。
其他认证源¶
有时候,您需要挂钩到另一个身份验证源 - 即用户名和密码或身份验证方法的另一个来源。
例如,您的公司可能已经有一个LDAP设置,为每个员工存储用户名和密码。如果用户在LDAP和基于Django的应用程序中有单独的帐户,这对网络管理员和用户本身来说都是一个麻烦。
因此,为了处理这种情况,Django身份验证系统允许您插入其他身份验证源。您可以覆盖Django的默认基于数据库的方案,或者您可以与其他系统一起使用默认系统。
有关Django附带的身份验证后端的信息,请参阅 认证后端引用。
指定身份验证后端¶
在幕后,Django维护着一个检查身份验证的“身份验证后端”列表。当有人调用 django.contrib.auth.authenticate()
时 - 如 如何记录用户 中所述 - Django尝试跨所有其身份验证后端进行身份验证。如果第一个认证方法失败,Django尝试第二个认证方法,依此类推,直到尝试所有后端。
要使用的身份验证后端列表在 AUTHENTICATION_BACKENDS
设置中指定。这应该是一个Python路径名列表,指向知道如何进行身份验证的Python类。这些类可以在Python路径上的任何位置。
默认情况下,AUTHENTICATION_BACKENDS
设置为:
['django.contrib.auth.backends.ModelBackend']
这是检查Django用户数据库并查询内置权限的基本身份验证后端。它不通过任何速率限制机制提供防止暴力攻击的保护。您可以在自定义验证后端实现自己的速率限制机制,也可以使用大多数Web服务器提供的机制。
AUTHENTICATION_BACKENDS
的顺序很重要,因此如果相同的用户名和密码在多个后端有效,Django将会在第一个肯定匹配时停止处理。
如果后端引发 PermissionDenied
异常,身份验证将立即失败。 Django不会检查后面的后端。
注解
一旦用户已经验证,Django存储哪些后端用于在用户的会话中验证用户,并且每当需要访问当前验证的用户时,在该会话的持续时间内重新使用相同的后端。这有效地意味着验证源是基于每个会话缓存的,因此如果您更改 AUTHENTICATION_BACKENDS
,您需要清除会话数据,如果您需要强制用户使用不同的方法重新验证。一个简单的方法就是执行 Session.objects.all().delete()
。
编写身份验证后端¶
认证后端是实现两个所需方法的类:get_user(user_id)
和 authenticate(**credentials)
,以及一组可选的与许可相关的 授权方法。
get_user
方法采用 user_id
- 它可以是用户名,数据库ID或其他,但必须是用户对象的主键 - 并返回一个用户对象。
authenticate
方法将凭据作为关键字参数。大多数时候,它看起来像这样:
class MyBackend(object):
def authenticate(self, username=None, password=None):
# Check the username/password and return a user.
...
但它也可以验证令牌,如此:
class MyBackend(object):
def authenticate(self, token=None):
# Check the token and return a user.
...
无论哪种方式,authenticate()
应检查其获取的凭据,如果凭据有效,则返回与这些凭证匹配的用户对象。如果它们无效,它应该返回 None
。
Django管理员与Django 用户对象 紧密耦合。处理这个问题的最佳方法是为每个存在于后端的用户创建一个Django User
对象(例如,在LDAP目录,外部SQL数据库等)。您可以编写一个脚本来提前做到这一点,或者您的 authenticate
方法可以在用户第一次登录时执行。
下面是一个示例后端,它根据您的 settings.py
文件中定义的用户名和密码变量进行身份验证,并在用户第一次验证时创建一个Django User
对象:
from django.conf import settings
from django.contrib.auth.hashers import check_password
from django.contrib.auth.models import User
class SettingsBackend(object):
"""
Authenticate against the settings ADMIN_LOGIN and ADMIN_PASSWORD.
Use the login name and a hash of the password. For example:
ADMIN_LOGIN = 'admin'
ADMIN_PASSWORD = 'pbkdf2_sha256$30000$Vo0VlMnkR4Bk$qEvtdyZRWTcOsCnI/oQ7fVOu1XAURIZYoOZ3iq8Dr4M='
"""
def authenticate(self, username=None, password=None):
login_valid = (settings.ADMIN_LOGIN == username)
pwd_valid = check_password(password, settings.ADMIN_PASSWORD)
if login_valid and pwd_valid:
try:
user = User.objects.get(username=username)
except User.DoesNotExist:
# Create a new user. There's no need to set a password
# because only the password from settings.py is checked.
user = User(username=username)
user.is_staff = True
user.is_superuser = True
user.save()
return user
return None
def get_user(self, user_id):
try:
return User.objects.get(pk=user_id)
except User.DoesNotExist:
return None
在自定义后端处理授权¶
自定义验证后端可以提供自己的权限。
用户模型将委派权限查找功能(get_group_permissions()
,get_all_permissions()
,has_perm()
和 has_module_perms()
)到实现这些功能的任何认证后端。
给予用户的权限将是所有后端返回的所有权限的超集。也就是说,Django向用户授予任何一个后端授予的权限。
如果后端在 has_perm()
或 has_module_perms()
中引发 PermissionDenied
异常,授权将立即失败,Django不会检查后续的后端。
上面的简单后端可以相当简单地实现魔术管理员的权限:
class SettingsBackend(object):
...
def has_perm(self, user_obj, perm, obj=None):
return user_obj.username == settings.ADMIN_LOGIN
这给上面示例中用户授予的访问权限的完全权限。请注意,除了给予相关的 django.contrib.auth.models.User
函数的相同参数之外,后端auth函数都将用户对象(可能是匿名用户)作为参数。
完全授权实现可以在 django/contrib/auth/backends.py 中的 ModelBackend
类中找到,这是默认后端,并且在大多数时间查询 auth_permission
表。如果您希望只为部分后端API提供自定义行为,则可以利用Python继承和子类 ModelBackend
,而不是在自定义后端实现完整的API。
匿名用户的授权¶
匿名用户是未认证的用户,即他们没有提供有效的身份验证详细信息。然而,这并不一定意味着他们没有授权做任何事情。在最基本的层面上,大多数网站授权匿名用户浏览大部分网站,许多网站允许匿名张贴评论等。
Django的权限框架没有地方可以存储匿名用户的权限。但是,传递给身份验证后端的用户对象可能是 django.contrib.auth.models.AnonymousUser
对象,允许后端为匿名用户指定自定义授权行为。这对于可重复使用的应用程序的作者特别有用,他们可以将所有授权问题委托给身份验证后端,而不需要设置来控制匿名访问。
非活动用户的授权¶
非活动用户是其 is_active
字段设置为 False
的用户。 ModelBackend
和 RemoteUserBackend
身份验证后端禁止这些用户进行身份验证。如果自定义用户模型没有 is_active
字段,则将允许所有用户进行身份验证。
如果要允许非活动用户进行身份验证,您可以使用 AllowAllUsersModelBackend
或 AllowAllUsersRemoteUserBackend
。
对权限系统中的匿名用户的支持允许匿名用户具有执行某些操作的权限的情况,而未被认证的用户不具有。
不要忘记在自己的后端权限方法中测试用户的 is_active
属性。
在旧版本中,ModelBackend
允许非活动用户进行身份验证。
处理对象权限¶
Django的权限框架有对象权限的基础,虽然在核心没有实现它。这意味着检查对象权限将总是返回 False
或空列表(取决于执行的检查)。认证后端将接收关于每个对象相关授权方法的关键字参数 obj
和 user_obj
,并且可以适当地返回对象级别许可。
自定义权限¶
要为给定模型对象创建自定义权限,请使用 permissions
模型元属性。
此示例任务模型创建三个自定义权限,即用户可以或不能对特定于您的应用程序的任务实例执行的操作:
class Task(models.Model):
...
class Meta:
permissions = (
("view_task", "Can see available tasks"),
("change_task_status", "Can change the status of tasks"),
("close_task", "Can remove a task by setting its status as closed"),
)
唯一做的是在运行 manage.py migrate
时创建这些额外的权限(创建权限的函数连接到 post_migrate
信号)。当用户尝试访问应用程序提供的功能(查看任务,更改任务状态,关闭任务)时,您的代码负责检查这些权限的值。继续上述示例,以下检查用户可以查看任务:
user.has_perm('app.view_task')
扩展现有的 User
模型¶
有两种方法来扩展默认的 User
模型,而不替换您自己的模型。如果所需的更改纯粹是行为,并且不需要对数据库中存储的内容进行任何更改,则可以基于 User
创建 代理模型。这允许由代理模型提供的任何特征,包括默认排序,自定义管理器或自定义模型方法。
如果您希望存储与 User
相关的信息,您可以对包含字段的模型使用 OneToOneField
以获取其他信息。这种一对一模型通常称为配置文件模型,因为它可能存储有关网站用户的非身份验证相关信息。例如,您可以创建一个Employee模型:
from django.contrib.auth.models import User
class Employee(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
department = models.CharField(max_length=100)
假设现有员工Fred Smith具有User和Employee模型,则可以使用Django的标准相关模型约定访问相关信息:
>>> u = User.objects.get(username='fsmith')
>>> freds_department = u.employee.department
要将配置文件模型的字段添加到管理员的用户页面,请在应用程序的 admin.py
中定义一个 InlineModelAdmin
(对于此示例,我们将使用 StackedInline
),并将其添加到已注册为 User
类的 UserAdmin
类中:
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
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(BaseUserAdmin):
inlines = (EmployeeInline, )
# Re-register UserAdmin
admin.site.unregister(User)
admin.site.register(User, UserAdmin)
这些配置文件模型在任何方面都不是特殊的 - 它们只是恰好与用户模型有一对一链接的Django模型。因此,它们不是在创建用户时自动创建的,而是可以使用 django.db.models.signals.post_save
来适当地创建或更新相关模型。
使用相关模型会产生其他查询或联接来检索相关数据。根据您的需要,包含相关字段的自定义用户模型可能是更好的选择,但是,与项目应用程序中的默认用户模型的现有关系可能证明额外的数据库负载。
替换自定义 User
模型¶
某些类型的项目可能具有认证要求,Django的内置 User
模型并不总是适合的。例如,在某些网站上,使用电子邮件地址作为标识令牌而不是用户名更有意义。
Django允许您通过为引用自定义模型的 AUTH_USER_MODEL
设置提供一个值来覆盖默认用户模型:
AUTH_USER_MODEL = 'myapp.MyUser'
这个虚线对描述了Django应用程序的名称(它必须在您的 INSTALLED_APPS
中),以及您希望用作用户模型的Django模型的名称。
在启动项目时使用自定义用户模型¶
如果您正在开始一个新项目,强烈建议您设置一个自定义用户模型,即使默认的 User
模型已经足够了。此模型的行为与默认用户模型相同,但如果需要,您可以在将来自定义它:
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
pass
不要忘了指向 AUTH_USER_MODEL
。在首次创建任何迁移或运行 manage.py migrate
之前执行此操作。
更改为自定义用户模型中期项目¶
在创建数据库表之后更改 AUTH_USER_MODEL
是非常困难的,因为它会影响外键和多对多关系。
此更改无法自动完成,需要手动修复模式,从旧用户表中移动数据,以及手动重新应用某些迁移。有关步骤的概述,请参阅 #25313。
由于Django对可交换模型的动态依赖关系功能的限制,AUTH_USER_MODEL
引用的模型必须在其应用程序(通常称为 0001_initial
)的第一次迁移中创建;否则,你会有依赖问题。
此外,在运行迁移时,您可能遇到 CircularDependencyError
,因为Django将无法自动断开依赖性循环,因为动态依赖关系。如果您看到此错误,您应该通过将依赖于您的用户模型的模型移动到第二次迁移来打破回路。 (你可以尝试使两个具有 ForeignKey
的正常模型,看看 makemigrations
如何解决循环依赖,如果你想看看它是如何做的通常做)。
可重复使用的应用和 AUTH_USER_MODEL
¶
可重复使用的应用不应实施自定义用户模型。项目可能使用许多应用程序,并且实施自定义用户模型的两个可重用应用程序不能一起使用。如果您需要在应用中存储每个用户的信息,请使用 ForeignKey
或 OneToOneField
到 settings.AUTH_USER_MODEL
,如下所述。
引用 User
模型¶
如果直接引用 User
(例如,通过在外键中引用它),您的代码将不会在 AUTH_USER_MODEL
设置更改为其他用户模型的项目中工作。
-
get_user_model
()¶ 而不是直接引用
User
,您应该使用django.contrib.auth.get_user_model()
引用用户模型。此方法将返回当前活动的用户模型 - 如果指定了自定义用户模型,则返回自定义用户模型,否则返回User
。当您向用户模型定义外键或多对多关系时,应使用
AUTH_USER_MODEL
设置指定自定义模型。例如:from django.conf import settings from django.db import models class Article(models.Model): author = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.CASCADE, )
当连接到用户模型发送的信号时,应使用
AUTH_USER_MODEL
设置指定自定义模型。例如:from django.conf import settings from django.db.models.signals import post_save def post_save_receiver(sender, instance, created, **kwargs): pass post_save.connect(post_save_receiver, sender=settings.AUTH_USER_MODEL)
一般来说,您应该在导入时执行的代码中使用
AUTH_USER_MODEL
设置引用用户模型。get_user_model()
只有在Django导入所有模型后才能工作。
指定自定义用户模型¶
模型设计注意事项
在处理与您的自定义用户模型中的认证不直接相关的信息之前,请仔细考虑。
将应用程序特定的用户信息存储在与用户模型有关系的模型中可能更好。这允许每个应用程序指定自己的用户数据要求,而不存在与其他应用程序冲突的风险。另一方面,检索此相关信息的查询将涉及数据库连接,这可能对性能有影响。
Django期望您的自定义用户模型满足一些最低要求。
如果使用默认身份验证后端,则模型必须具有一个可用于识别目的的唯一字段。这可以是用户名,电子邮件地址或任何其他唯一属性。如果您使用可以支持它的自定义身份验证后端,则允许使用非唯一用户名字段。
您的模型必须提供一种以“短”和“长”形式处理用户的方法。对此最常见的解释是使用用户的给定名称作为“短”标识符,以及用户的全名作为“长”标识符。但是,这两个方法返回的内容没有约束 - 如果需要,它们可以返回完全相同的值。
构建兼容的自定义用户模型的最简单的方法是继承 AbstractBaseUser
。 AbstractBaseUser
提供了用户模型的核心实现,包括散列密码和令牌化密码重置。然后,您必须提供一些关键的实施详细信息:
-
class
models.
CustomUser
¶ -
USERNAME_FIELD
¶ 描述用户模型上用作唯一标识符的字段名称的字符串。这通常是某种用户名,但它也可以是电子邮件地址或任何其他唯一标识符。字段 must 是唯一的(即,在其定义中设置了
unique=True
),除非您使用可以支持非唯一用户名的自定义认证后端。在以下示例中,字段
identifier
用作标识字段:class MyUser(AbstractBaseUser): identifier = models.CharField(max_length=40, unique=True) ... USERNAME_FIELD = 'identifier'
USERNAME_FIELD
现在支持ForeignKey
s。由于在createsuperuser
提示期间没有办法传递模型实例,因此希望用户输入现有实例的to_field
值(默认为primary_key
)的值。
-
REQUIRED_FIELDS
¶ 通过
createsuperuser
管理命令创建用户时将提示的字段名称列表。将提示用户为这些字段中的每一个提供值。它必须包括blank
为False
或未定义的任何字段,并且可以包括在以交互方式创建用户时要提示的其他字段。REQUIRED_FIELDS
在Django的其他部分没有效果,例如在管理员中创建用户。例如,下面是一个定义了两个必填字段用户模型部分定义 - 诞生和高度的日期:
class MyUser(AbstractBaseUser): ... date_of_birth = models.DateField() height = models.FloatField() ... REQUIRED_FIELDS = ['date_of_birth', 'height']
注解
REQUIRED_FIELDS
必须包含用户模型上的所有必填字段,但 not 应包含USERNAME_FIELD
或password
,因为将始终提示输入这些字段。REQUIRED_FIELDS
现在支持ForeignKey
s。由于在createsuperuser
提示期间没有办法传递模型实例,因此希望用户输入现有实例的to_field
值(默认为primary_key
)的值。
-
is_active
¶ 指示用户是否被视为“活动”的布尔属性。此属性作为默认为
True
的AbstractBaseUser
上的属性提供。如何选择实施它取决于您选择的身份验证后端的详细信息。有关详细信息,请参阅is_active attribute on the built-in user model
的文档。
-
get_full_name
()¶ 用户的更长的正式标识符。常见的解释是用户的全名,但它可以是标识用户的任何字符串。
-
get_short_name
()¶ 用户的简短,非正式标识符。常见的解释是用户的名字,但它可以是以非正式方式标识用户的任何字符串。它也可以返回与
django.contrib.auth.models.User.get_full_name()
相同的值。
导入
AbstractBaseUser
New in Django 1.9.AbstractBaseUser
和BaseUserManager
可以从django.contrib.auth.base_user
导入,因此可以导入它们,而不在INSTALLED_APPS
中包含django.contrib.auth
(这在旧版本中引发了弃用警告,并且在Django 1.9中不再支持)。-
以下属性和方法可用于 AbstractBaseUser
的任何子类:
-
class
models.
AbstractBaseUser
¶ -
get_username
()¶ 返回由
USERNAME_FIELD
指定的字段的值。
-
clean
()¶ - New in Django 1.10.
通过调用
normalize_username()
来规范化用户名。如果你重写这个方法,一定要调用super()
来保持规范化。
-
classmethod
normalize_username
(username)¶ - New in Django 1.10.
将NFKC Unicode规范化应用于用户名,以使具有不同Unicode代码点的视觉相同的字符被视为相同。
-
is_authenticated
¶ 只读属性总是
True
(与总是False
的AnonymousUser.is_authenticated
相对)。这是一种判断用户是否已通过身份验证的方法。这并不意味着任何权限,也不检查用户是否处于活动状态或有效会话。即使通常你会在request.user
上检查这个属性,以确定它是否由AuthenticationMiddleware
填充(代表当前登录的用户),你应该知道这个属性是任何User
实例的True
。Changed in Django 1.10:在旧版本中,这是一种方法。使用它作为方法的向后兼容性支持将在Django 2.0中删除。
-
is_anonymous
¶ 只读属性,始终为
False
。这是区分User
和AnonymousUser
对象的一种方法。一般来说,您应该优先使用is_authenticated
此属性。Changed in Django 1.10:在旧版本中,这是一种方法。使用它作为方法的向后兼容性支持将在Django 2.0中删除。
-
set_password
(raw_password)¶ 将用户的密码设置为给定的原始字符串,注意密码哈希。不保存
AbstractBaseUser
对象。当raw_password为
None
时,密码将设置为不可用的密码,如同使用set_unusable_password()
。
-
check_password
(raw_password)¶ 如果给定的原始字符串是用户的正确密码,则返回
True
。 (这在进行比较时负责密码哈希的处理。)
-
set_unusable_password
()¶ 将用户标记为没有设置密码。这与为密码使用空白字符串不同。该用户的
check_password()
永远不会返回True
。不保存AbstractBaseUser
对象。如果针对现有外部源(例如LDAP目录)进行应用程序的身份验证,则可能需要这样做。
-
has_usable_password
()¶ 如果为此用户调用了
set_unusable_password()
,则返回False
。
-
您还应为用户模型定义自定义管理器。如果您的用户模型定义与Django的默认用户相同的 username
,email
,is_staff
,is_active
,is_superuser
,last_login
和 date_joined
字段,您可以只安装Django的 UserManager
;但是,如果您的用户模型定义了不同的字段,则需要定义扩展 BaseUserManager
的自定义管理器,并提供另外两种方法:
-
class
models.
CustomUserManager
¶ -
create_user
(*username_field*, password=None, **other_fields)¶ create_user()
的原型应该接受用户名字段,加上所有必需的字段作为参数。例如,如果您的用户模型使用email
作为用户名字段,并且具有date_of_birth
作为必填字段,则create_user
应定义为:def create_user(self, email, date_of_birth, password=None): # create user here ...
-
create_superuser
(*username_field*, password, **other_fields)¶ create_superuser()
的原型应该接受用户名字段,加上所有必需的字段作为参数。例如,如果您的用户模型使用email
作为用户名字段,并且具有date_of_birth
作为必填字段,则create_superuser
应定义为:def create_superuser(self, email, date_of_birth, password): # create superuser here ...
与
create_user()
不同,create_superuser()
must 要求调用方提供密码。
-
BaseUserManager
提供了以下实用程序方法:
-
class
models.
BaseUserManager
¶ -
classmethod
normalize_email
(email)¶ 通过缩小电子邮件地址的域部分来规范化电子邮件地址。
-
get_by_natural_key
(username)¶ 使用由
USERNAME_FIELD
指定的字段的内容检索用户实例。
-
make_random_password
(length=10, allowed_chars='abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789')¶ 返回具有给定长度和给定字符串的允许字符的随机密码。请注意,
allowed_chars
的默认值不包含可能导致用户混淆的字母,包括:i
,l
,I
和1
(小写字母i,小写字母L,大写字母i和数字1)o
,O
和0
(小写字母o,大写字母o和零)
-
classmethod
扩展Django的默认 User
¶
如果你对Django的 User
模型完全满意,并且你只想添加一些附加的配置文件信息,你可以简单地子类化 django.contrib.auth.models.AbstractUser
并添加你的自定义配置文件字段,虽然我们建议一个单独的模型,如“模型设计注意事项”注意 指定自定义用户模型。 AbstractUser
提供了默认 User
作为 抽象模型 的完全实现。
自定义用户和内置的身份验证表单¶
Django的内置 形式 和 视图 对他们正在使用的用户模型做出某些假设。
以下形式与 AbstractBaseUser
的任何子类兼容:
AuthenticationForm
:使用USERNAME_FIELD
指定的用户名字段。
以下表单假设用户模型,如果满足这些假设,可以按原样使用:
PasswordResetForm
:假定用户模型具有可用于标识用户的名为email
的字段和名为is_active
的布尔字段,以防止对非活动用户进行密码重置。
最后,以下表单与 User
相关,需要重新编写或扩展以使用自定义用户模型:
如果您的自定义用户模型是 AbstractUser
的一个简单子类,那么您可以以这种方式扩展这些表单:
from django.contrib.auth.forms import UserCreationForm
from myapp.models import CustomUser
class CustomUserCreationForm(UserCreationForm):
class Meta(UserCreationForm.Meta):
model = CustomUser
fields = UserCreationForm.Meta.fields + ('custom_field',)
自定义用户和 django.contrib.admin
¶
如果希望自定义用户模型也与管理员一起使用,则用户模型必须定义一些其他属性和方法。这些方法允许管理员控制用户对管理内容的访问:
-
class
models.
CustomUser
-
is_staff
¶ 如果允许用户访问管理网站,则返回
True
。
-
is_active
¶ 如果用户帐户当前处于活动状态,则返回
True
。
-
has_perm(perm, obj=None):
如果用户具有命名权限,则返回
True
。如果提供了obj
,则需要针对特定对象实例检查权限。
-
has_module_perms(app_label):
如果用户具有访问给定应用程序中的模型的权限,则返回
True
。
您还需要使用管理员注册您的自定义用户模型。如果您的自定义用户模型扩展了 django.contrib.auth.models.AbstractUser
,则可以使用Django现有的 django.contrib.auth.admin.UserAdmin
类。但是,如果您的用户模型扩展了 AbstractBaseUser
,则需要定义一个自定义 ModelAdmin
类。可以将默认 django.contrib.auth.admin.UserAdmin
子类化;但是,您需要覆盖引用 django.contrib.auth.models.AbstractUser
上不在自定义用户类上的字段的任何定义。
自定义用户和权限¶
为了方便将Django的权限框架包含到你自己的用户类中,Django提供了 PermissionsMixin
。这是一个抽象模型,可以包含在用户模型的类层次结构中,为您提供支持Django权限模型所需的所有方法和数据库字段。
PermissionsMixin
提供以下方法和属性:
-
class
models.
PermissionsMixin
¶ -
is_superuser
¶ 布尔值。指定此用户具有所有权限,而不显式分配它们。
-
get_group_permissions
(obj=None)¶ 通过用户的组返回用户拥有的一组权限字符串。
如果传入
obj
,则仅返回此特定对象的组权限。
-
get_all_permissions
(obj=None)¶ 通过组和用户权限返回用户拥有的一组权限字符串。
如果传入
obj
,则只返回此特定对象的权限。
-
has_perm
(perm, obj=None)¶ 如果用户具有指定的权限,则返回
True
,其中perm
的格式为"<app label>.<permission codename>"
(请参见 权限)。如果用户不活动,此方法将始终返回False
。如果传递
obj
,此方法将不检查模型的权限,但是检查此特定对象。
-
has_perms
(perm_list, obj=None)¶ 如果用户具有每个指定的权限,则返回
True
,其中每个perm的格式为"<app label>.<permission codename>"
。如果用户处于非活动状态,则此方法将始终返回False
。如果传递
obj
,此方法将不检查模型的权限,但是检查特定对象的权限。
-
has_module_perms
(package_name)¶ 如果用户在给定包(Django应用标签)中有任何权限,则返回
True
。如果用户处于非活动状态,则此方法将始终返回False
。
-
自定义用户和代理模型¶
自定义用户模型的一个限制是安装自定义用户模型将破坏扩展 User
的任何代理模型。代理模型必须基于具体的基类;通过定义自定义用户模型,您可以删除Django可靠地识别基类的能力。
如果项目使用代理模型,则必须修改代理以扩展项目中使用的用户模型,或将代理的行为合并到 User
子类中。
一个完整的例子¶
以下是与管理员兼容的自定义用户应用程序的示例。此用户模型使用电子邮件地址作为用户名,并具有所需的出生日期;它不提供任何权限检查,超出用户帐户上的一个简单的 admin
标志。此模型将与所有内置的身份验证表单和视图兼容,但用户创建表单除外。此示例说明大多数组件如何协同工作,但不打算直接复制到项目中供生产使用。
此代码将全部存储在自定义认证应用程序的 models.py
文件中:
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
然后,要使用Django的管理员注册此自定义用户模型,应用程序的 admin.py
文件中将需要以下代码:
from django import forms
from django.contrib import admin
from django.contrib.auth.models import Group
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
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 UserAdmin(BaseUserAdmin):
# 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, UserAdmin)
# ... 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'