使用 Django 的验证系统¶
本文档介绍了 Django 验证系统在默认配置下的使用方法。默认配置满足最常见的项目需求,可以处理相当多的任务,还有一个安全的密码和权限实现。对于验证需求与默认配置不同的项目,Django 支持对身份验证进行扩展和定制。
Django 验证同时提供身份验证和授权,通常称为身份验证系统,因为这些功能在某种程度上是耦合的。
User
对象¶
User
对象是认证系统的核心。它们通常代表与你网站进行交互的人,并用于启用诸如限制访问、注册用户配置文件、将内容与创建者关联等功能。在 Django 的认证框架中只存在一个用户类,即 'superusers'
或管理员 'staff'
用户只是具有特殊属性设置的用户对象,而不是不同类别的用户对象。
默认用户的主要属性是:
请查看 完整的 API 文档
以获取详细的参考信息,接下来的文档更偏向于任务导向。
创建用户¶
创建用户的最直接方法是使用包含的 create_user()
辅助函数:
>>> from django.contrib.auth.models import User
>>> user = User.objects.create_user("john", "lennon@thebeatles.com", "johnpassword")
# At this point, user is a User object that has already been saved
# to the database. You can continue to change its attributes
# if you want to change other fields.
>>> user.last_name = "Lennon"
>>> user.save()
如果你安装了 Django 管理界面,你还可以 以交互方式创建用户。
创建超级用户¶
使用 createsuperuser
命令创建超级用户:
$ python manage.py createsuperuser --username=joe --email=joe@example.com
...\> py manage.py createsuperuser --username=joe --email=joe@example.com
你将被要求输入密码。输入密码后,用户将立即创建。如果你不使用 --username
或 --email
选项,它将提示你输入这些值。
更改密码¶
Django 不会在用户模型上存储原始(明文)密码,而只会存储散列值(详细信息请参阅 有关密码管理的文档)。因此,请不要尝试直接操作用户的密码属性。这就是在创建用户时使用辅助函数的原因。
更改一个用户的密码,你有几个选择:
manage.py changepassword *username*
提供了一种从命令行更改用户密码的方法。它会提示您更改指定用户的密码,你必须输入两次密码。如果它们都匹配,新密码将立即更改。如果你不提供用户名,该命令将尝试更改与当前系统用户匹配的用户名的密码。
你也可以通过编程方式更改密码,使用 set_password()
:
>>> from django.contrib.auth.models import User
>>> u = User.objects.get(username="john")
>>> u.set_password("new password")
>>> u.save()
如果你已安装了 Django 管理员界面,你也可以在 身份验证系统的管理员页面 上更改用户的密码。
Django 还提供了可以用于允许用户更改自己密码的 视图 和 表单。
更改用户的密码将注销其所有会话。请参阅 password-change-session-invalidation 以获取详细信息。
验证用户¶
-
aauthenticate
(request=None, **credentials)¶ 异步版本:
aauthenticate()
使用
authenticate()
来验证一组凭据。它以关键字参数的形式接受凭据,对于默认情况下,是username
和password
,然后检查它们与每个 认证后端 进行匹配,如果凭据对于某个后端是有效的,则返回一个User
对象。如果凭据对于任何后端都无效,或者如果后端引发PermissionDenied
,则返回None
。例如:from django.contrib.auth import authenticate user = authenticate(username="john", password="secret") if user is not None: # A backend authenticated the credentials ... else: # No backend authenticated the credentials ...
request
是一个可选的HttpRequest
,它被传递给身份验证后端的authenticate()
方法。备注
这是一种低级的验证一组凭据的方法;例如,它被
RemoteUserMiddleware
使用。除非你正在编写自己的身份验证系统,否则你可能不会使用它。如果你想要登录用户,最好使用LoginView
。Changed in Django 5.0:aauthenticate()
函数已添加。
权限和认证¶
Django 自带了一个内置的权限系统。它提供了一种将权限分配给特定用户和用户组的方式。
它被 Django 管理站点使用,但欢迎你在自己的代码中使用它。
Django 管理站点使用权限如下:
- 查看对象的访问权限仅限于具有该类型对象的 "view" 或 "change" 权限的用户。
- 查看 "添加" 表单并添加对象的访问权限仅限于具有该类型对象的 "add" 权限的用户。
- 访问更改列表、查看 "更改" 表单和更改对象的权限仅限于具有该类型对象的 "change" 权限的用户。
- 删除对象的权限仅限于具有该类型对象的 "delete" 权限的用户。
权限不仅可以按对象类型设置,还可以按特定对象实例设置。通过使用 ModelAdmin
类提供的 has_view_permission()
、has_add_permission()
、has_change_permission()
和 has_delete_permission()
方法,可以为相同类型的不同对象实例自定义权限。
User
对象有两个多对多字段:groups
和 user_permissions
。User
对象可以以与任何其他 Django 模型 相同的方式访问其相关对象:
myuser.groups.set([group_list])
myuser.groups.add(group, group, ...)
myuser.groups.remove(group, group, ...)
myuser.groups.clear()
myuser.user_permissions.set([permission_list])
myuser.user_permissions.add(permission, permission, ...)
myuser.user_permissions.remove(permission, permission, ...)
myuser.user_permissions.clear()
默认权限¶
当在你的 INSTALLED_APPS
配置中列出了 django.contrib.auth
时,它会确保为每个在你已安装的应用程序中定义的 Django 模型创建四个默认权限:添加、更改、删除和查看。
这些权限将在运行 manage.py migrate
时创建;在将 django.contrib.auth
添加到 INSTALLED_APPS
后首次运行 migrate
时,将为所有先前安装的模型创建默认权限,以及在此时安装的任何新模型。此后,每次运行 manage.py migrate
时(创建权限的函数与 post_migrate
信号相关联),都会为新模型创建默认权限。
假设你有一个带有 app_label
为 foo
的应用程序,并且有一个名为 Bar
的模型,要测试基本权限,你应该使用以下方式:
- 添加:
user.has_perm('foo.add_bar')
- 修改:
user.has_perm('foo.change_bar')
- 删除:
user.has_perm('foo.delete_bar')
- 查看:
user.has_perm('foo.view_bar')
Permission
模型很少直接访问。
组¶
django.contrib.auth.models.Group
模型是一种通用的方式,用于对用户进行分类,以便可以为这些用户分配权限或其他标签。一个用户可以属于任意数量的组。
属于组的用户自动拥有分配给该组的权限。例如,如果组 Site editors
具有权限 can_edit_home_page
,那么该组中的任何用户都将具有该权限。
除了权限外,组是一种方便的方式,用于对用户进行分类并为他们提供一些标签或扩展功能。例如,你可以创建一个组 'Special users'
,然后编写代码,可以让他们访问你网站的会员专区,或者发送给他们会员专用的电子邮件消息。
以编程方式创建权限¶
虽然 自定义权限 可以在模型的 Meta
类中定义,但你也可以直接创建权限。例如,你可以为 myapp
中的 BlogPost
模型创建 can_publish
权限:
from myapp.models import BlogPost
from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType
content_type = ContentType.objects.get_for_model(BlogPost)
permission = Permission.objects.create(
codename="can_publish",
name="Can Publish Posts",
content_type=content_type,
)
然后可以通过将权限分配给 User
的 user_permissions
属性或 Group
的 permissions
属性来分配该权限。
代理模型需要有自己的内容类型
如果你想为代理模型创建 权限,请将 for_concrete_model=False
传递给 ContentTypeManager.get_for_model()
以获取适当的 ContentType
:
content_type = ContentType.objects.get_for_model(
BlogPostProxy, for_concrete_model=False
)
权限缓存¶
ModelBackend
在第一次需要获取权限进行权限检查后,会将权限缓存到用户对象上。这通常在请求-响应循环中是没问题的,因为权限通常不会在添加后立即进行检查(例如,在管理界面中)。如果你正在添加权限并立即在测试或视图中进行检查,最简单的解决方法是重新从数据库中获取用户。例如:
from django.contrib.auth.models import Permission, User
from django.contrib.contenttypes.models import ContentType
from django.shortcuts import get_object_or_404
from myapp.models import BlogPost
def user_gains_perms(request, user_id):
user = get_object_or_404(User, pk=user_id)
# any permission check will cache the current set of permissions
user.has_perm("myapp.change_blogpost")
content_type = ContentType.objects.get_for_model(BlogPost)
permission = Permission.objects.get(
codename="change_blogpost",
content_type=content_type,
)
user.user_permissions.add(permission)
# Checking the cached permission set
user.has_perm("myapp.change_blogpost") # False
# Request new instance of User
# Be aware that user.refresh_from_db() won't clear the cache.
user = get_object_or_404(User, pk=user_id)
# Permission cache is repopulated from the database
user.has_perm("myapp.change_blogpost") # True
...
代理模型¶
代理模型的工作方式和具体模型完全相同。代理模型使用自己的内容类型创建权限。代理模型不会继承其子类的具体模型权限。
class Person(models.Model):
class Meta:
permissions = [("can_eat_pizzas", "Can eat pizzas")]
class Student(Person):
class Meta:
proxy = True
permissions = [("can_deliver_pizzas", "Can deliver pizzas")]
>>> # Fetch the content type for the proxy model.
>>> content_type = ContentType.objects.get_for_model(Student, for_concrete_model=False)
>>> student_permissions = Permission.objects.filter(content_type=content_type)
>>> [p.codename for p in student_permissions]
['add_student', 'change_student', 'delete_student', 'view_student',
'can_deliver_pizzas']
>>> for permission in student_permissions:
... user.user_permissions.add(permission)
...
>>> user.has_perm("app.add_person")
False
>>> user.has_perm("app.can_eat_pizzas")
False
>>> user.has_perms(("app.add_student", "app.can_deliver_pizzas"))
True
Web 请求的认证¶
Django 使用 会话 和中间件将身份验证系统与 请求对象
钩连在一起。
这些提供了每个请求上的 request.user
属性和 request.auser
异步方法,表示当前用户。如果当前用户未登录,这个属性将被设置为 AnonymousUser
的实例,否则它将是 User
的实例。
你可以使用 is_authenticated
来区分它们,如下所示:
if request.user.is_authenticated:
# Do something for authenticated users.
...
else:
# Do something for anonymous users.
...
或者在一个异步视图中:
user = await request.auser()
if user.is_authenticated:
# Do something for authenticated users.
...
else:
# Do something for anonymous users.
...
已添加了 HttpRequest.auser()
方法。
如何登录用户¶
如果你有一个已经经过认证的用户,想要将其附加到当前会话中,可以使用 login()
函数来实现。
-
alogin
(request, user, backend=None)¶ 异步版本:
alogin()
要从视图中登录用户,请使用
login()
。它接受一个HttpRequest
对象和一个User
对象。login()
会使用 Django 的会话框架将用户的ID保存在会话中。请注意,在用户登录后,匿名会话期间设置的任何数据都将保留在会话中。
以下示例展示了如何同时使用
authenticate()
和login()
:from django.contrib.auth import authenticate, login def my_view(request): username = request.POST["username"] password = request.POST["password"] user = authenticate(request, username=username, password=password) if user is not None: login(request, user) # Redirect to a success page. ... else: # Return an 'invalid login' error message. ...
Changed in Django 5.0:alogin()
函数已添加。
选择认证后端¶
当用户登录时,用户的 ID 和用于认证的后端被保存在用户的会话中。这允许相同的 认证后端 在将来的请求中获取用户的详细信息。要保存在会话中的认证后端是按以下方式选择的:
- 使用可选的
backend
参数的值,如果提供了的话。 - 使用
user.backend
属性的值,如果存在的话。这允许将authenticate()
和login()
配对使用:authenticate()
会在返回的用户对象上设置user.backend
属性。 - 如果只有一个
AUTHENTICATION_BACKENDS
中的后端,则使用其中的后端。 - 否则,抛出一个异常。
在情况 1 和情况 2 中,backend
参数的值或 user.backend
属性应该是一个点分路径字符串(类似于 AUTHENTICATION_BACKENDS
中的路径),而不是实际的后端类。
如何登出用户¶
-
alogout
(request)¶ 异步版本:
alogout()
要登出通过
django.contrib.auth.login()
登录的用户,请在你的视图中使用django.contrib.auth.logout()
。它接受一个HttpRequest
对象,没有返回值。示例:from django.contrib.auth import logout def logout_view(request): logout(request) # Redirect to a success page.
请注意,如果用户未登录,
logout()
不会引发任何错误。当你调用
logout()
时,当前请求的会话数据将被完全清除。所有现有的数据都会被移除。这是为了防止其他人使用同一个网络浏览器登录并访问之前用户的会话数据。如果你想将任何内容放入会话中,以便用户在注销后立即使用,请在调用logout()
之后执行这个操作。Changed in Django 5.0:alogout()
函数已添加。
限制访问仅限已登录用户¶
原始方式¶
限制访问页面的原始方法是检查 request.user.is_authenticated
并且要么重定向到登录页面:
from django.conf import settings
from django.shortcuts import redirect
def my_view(request):
if not request.user.is_authenticated:
return redirect(f"{settings.LOGIN_URL}?next={request.path}")
# ...
...或者显示一个错误消息:
from django.shortcuts import render
def my_view(request):
if not request.user.is_authenticated:
return render(request, "myapp/login_error.html")
# ...
login_required
装饰器¶
-
login_required
(redirect_field_name='next', login_url=None)[源代码]¶ 作为一种快捷方式,你可以使用方便的
login_required()
装饰器:from django.contrib.auth.decorators import login_required @login_required def my_view(request): ...
login_required()
会执行以下操作:- 如果用户没有登录,重定向到
settings.LOGIN_URL
,在查询字符串中传递当前的绝对路径。例如:/accounts/login/?next=/polls/3/
。 - 如果用户已登录,正常执行视图。视图代码可以自由地假设用户已登录。
默认情况下,用户成功认证后应重定向到的路径存储在名为
"next"
的查询字符串参数中。如果你想要使用不同的参数名称,login_required()
接受一个可选的redirect_field_name
参数:from django.contrib.auth.decorators import login_required @login_required(redirect_field_name="my_redirect_field") def my_view(request): ...
请注意,如果你为
redirect_field_name
提供了一个值,你很可能还需要自定义你的登录模板,因为存储重定向路径的模板上下文变量将使用redirect_field_name
的值作为其键,而不是(默认值)"next"
。login_required()
还接受一个可选的login_url
参数。示例:from django.contrib.auth.decorators import login_required @login_required(login_url="/accounts/login/") def my_view(request): ...
请注意,如果你没有指定``login_url``参数,你需要确保
settings.LOGIN_URL
和你的登录视图正确关联。例如,使用默认值,将以下行添加到你的 URL 配置文件中:from django.contrib.auth import views as auth_views path("accounts/login/", auth_views.LoginView.as_view()),
settings.LOGIN_URL
也接受视图函数名称和 命名的 URL 模式。这允许你在 URL 配置中自由重新映射你的登录视图,而无需更新该设置。- 如果用户没有登录,重定向到
备注
login_required
装饰器并不检查用户的 is_active
标志,但默认的 AUTHENTICATION_BACKENDS
会拒绝非活跃用户。
参见
如果你正在为 Django 的管理编写自定义视图(或者需要与内置视图使用相同的授权检查),你可能会发现 django.contrib.admin.views.decorators.staff_member_required()
装饰器是一个有用的替代选择,而不是使用 login_required()
。
已添加对包装异步视图函数的支持。
LoginRequiredMixin
混合类¶
当使用 基于类的视图 时,你可以通过使用 LoginRequiredMixin
来实现与 login_required
相同的行为。这个混合类应该在继承列表的最左边位置。
-
class
LoginRequiredMixin
[源代码]¶ 如果一个视图使用了这个混合类,所有非经过身份验证的用户的请求将被重定向到登录页面或显示 HTTP 403 禁止访问的错误,这取决于
raise_exception
参数的设置。你可以设置
AccessMixin
的任何参数来自定义未经授权用户的处理方式:from django.contrib.auth.mixins import LoginRequiredMixin class MyView(LoginRequiredMixin, View): login_url = "/login/" redirect_field_name = "redirect_to"
备注
与``login_required``装饰器一样,这个混合类也不会检查用户的``is_active``标志,但默认的 AUTHENTICATION_BACKENDS
会拒绝非活跃用户。
The login_not_required
decorator¶
When LoginRequiredMiddleware
is
installed, all views require authentication by default. Some views, such as the
login view, may need to disable this behavior.
-
login_not_required
()[源代码]¶ Allows unauthenticated requests to this view when
LoginRequiredMiddleware
is installed.
限制只有经过测试的已登录用户才能访问¶
要基于特定权限或其他测试来限制访问,你可以基本上按照前一节所述的方法进行操作。
你可以直接在视图中对 request.user
进行测试。例如,这个视图检查用户是否拥有所需域中的电子邮件,如果没有,就重定向到登录页面:
from django.shortcuts import redirect
def my_view(request):
if not request.user.email.endswith("@example.com"):
return redirect("/login/?next=%s" % request.path)
# ...
-
user_passes_test
(test_func, login_url=None, redirect_field_name='next')[源代码]¶ 作为一种快捷方式,你可以使用方便的
user_passes_test
装饰器,当可调用对象返回False
时,它会执行重定向:from django.contrib.auth.decorators import user_passes_test def email_check(user): return user.email.endswith("@example.com") @user_passes_test(email_check) def my_view(request): ...
user_passes_test()
接受一个必需的参数,即一个可调用对象,该对象接受一个User
对象,并在用户被允许查看页面时返回True
。请注意,user_passes_test()
不会自动检查User
是否是匿名用户。user_passes_test()
接受两个可选参数:login_url
- 允许你指定未通过测试的用户将被重定向到的 URL。如果你没有指定一个 URL,它可能是一个登录页面,并且默认为
settings.LOGIN_URL
。 redirect_field_name
- 与
login_required()
类似。将其设置为None
将其从 URL 中移除,如果你将不通过测试的用户重定向到一个没有 "下一页" 的非登录页面,你可能会想这样做。
例如:
@user_passes_test(email_check, login_url="/login/") def my_view(request): ...
Changed in Django 5.1:Support for wrapping asynchronous view functions and using asynchronous test callables was added.
-
class
UserPassesTestMixin
[源代码]¶ 当使用 基于类的视图 时,你可以使用
UserPassesTestMixin
来实现这一点。-
test_func
()[源代码]¶ 你需要重写类的
test_func()
方法来提供要执行的测试。此外,你可以设置AccessMixin
的任何参数来自定义未经授权用户的处理方式:from django.contrib.auth.mixins import UserPassesTestMixin class MyView(UserPassesTestMixin, View): def test_func(self): return self.request.user.email.endswith("@example.com")
-
get_test_func
()[源代码]¶ 你还可以重写
get_test_func()
方法,以便混合类使用不同名称的函数进行检查(而不是test_func()
)。
集成
UserPassesTestMixin
由于
UserPassesTestMixin
的实现方式,你不能在继承列表中堆叠它们。以下方式是不起作用的:class TestMixin1(UserPassesTestMixin): def test_func(self): return self.request.user.email.endswith("@example.com") class TestMixin2(UserPassesTestMixin): def test_func(self): return self.request.user.username.startswith("django") class MyView(TestMixin1, TestMixin2, View): ...
如果
TestMixin1
调用了super()
并考虑了该结果,那么TestMixin1
将不再能够独立工作。-
permission_required
装饰器¶
-
permission_required
(perm, login_url=None, raise_exception=False)[源代码]¶ It's a relatively common task to check whether a user has a particular permission. For that reason, Django provides a shortcut for that case: the
permission_required()
decorator:from django.contrib.auth.decorators import permission_required @permission_required("polls.add_choice") def my_view(request): ...
就像
has_perm()
方法一样,权限名称采用形式"<app label>.<permission codename>"
(例如,在polls
应用程序中的模型上的权限为polls.add_choice
)。该装饰器还可以接受一个权限的可迭代对象,此时用户必须具备所有这些权限才能访问视图。
请注意,
permission_required()
还接受一个可选的login_url
参数:from django.contrib.auth.decorators import permission_required @permission_required("polls.add_choice", login_url="/loginpage/") def my_view(request): ...
与
login_required()
装饰器一样,login_url
默认为settings.LOGIN_URL
。如果提供了
raise_exception
参数,装饰器将引发PermissionDenied
异常,而不是重定向到登录页面,从而触发 403(HTTP Forbidden)视图。如果你想使用
raise_exception
,但也想让用户有机会首先登录,你可以添加login_required()
装饰器:from django.contrib.auth.decorators import login_required, permission_required @login_required @permission_required("polls.add_choice", raise_exception=True) def my_view(request): ...
这也可以避免在
LoginView
的redirect_authenticated_user=True
且已登录用户没有所有所需权限时发生重定向循环。
已添加对包装异步视图函数的支持。
PermissionRequiredMixin
Mixin¶
要将权限检查应用于 基于类的视图,你可以使用 PermissionRequiredMixin
:
-
class
PermissionRequiredMixin
[源代码]¶ 这个混合类,就像
permission_required
装饰器一样,检查访问视图的用户是否具有所有给定的权限。你应该使用permission_required
参数来指定权限(或权限的可迭代对象):from django.contrib.auth.mixins import PermissionRequiredMixin class MyView(PermissionRequiredMixin, View): permission_required = "polls.add_choice" # Or multiple of permissions: permission_required = ["polls.view_choice", "polls.change_choice"]
你可以设置
AccessMixin
的任何参数来自定义未经授权用户的处理方式。你可能同样需要重写这些方法:
-
has_permission
()[源代码]¶ 返回一个布尔值,表示当前用户是否具有执行装饰视图的权限。默认情况下,这将返回调用
has_perms()
与get_permission_required()
返回的权限列表的结果。
-
在基于类的视图中重定向未经授权的请求¶
为了简化在 基于类的视图 中处理访问限制,可以使用 AccessMixin
来配置在拒绝访问时视图的行为。已认证的用户将被拒绝访问,并收到 HTTP 403 禁止访问的响应。匿名用户将根据 raise_exception
属性的设置,被重定向到登录页面或显示 HTTP 403 禁止访问的响应。
-
class
AccessMixin
[源代码]¶ -
login_url
¶ get_login_url()
方法的默认返回值。如果未设置,默认为None
,在这种情况下,get_login_url()
会回退到settings.LOGIN_URL
。
-
permission_denied_message
¶ get_permission_denied_message()
方法的默认返回值。默认为一个空字符串。
-
redirect_field_name
¶ get_redirect_field_name()
方法的默认返回值。默认为"next"
。
-
raise_exception
¶ 如果将此属性设置为
True
,则在不满足条件时会引发PermissionDenied
异常。当为 ``False``(默认值)时,匿名用户将被重定向到登录页面。
-
get_login_url
()[源代码]¶ 返回未通过测试的用户将被重定向到的URL。如果设置了
login_url
,则返回它,否则返回settings.LOGIN_URL
。
-
get_permission_denied_message
()[源代码]¶ 当
raise_exception
为True
时,可以使用此方法来控制传递给错误处理程序以显示给用户的错误消息。默认情况下返回permission_denied_message
属性。
-
get_redirect_field_name
()[源代码]¶ 返回应包含用户成功登录后应重定向到的URL的查询参数的名称。如果将其设置为
None
,则不会添加查询参数。默认情况下返回redirect_field_name
属性。
-
handle_no_permission
()[源代码]¶ 根据
raise_exception
的值,该方法要么引发PermissionDenied
异常,要么将用户重定向到login_url
,如果设置了redirect_field_name
,还可以包括它。
-
密码更改时的会话失效¶
如果你的 AUTH_USER_MODEL
继承自 AbstractBaseUser
或实现了自己的 get_session_auth_hash()
方法,已认证的会话将包括该函数返回的哈希值。在 AbstractBaseUser
的情况下,这是密码字段的 HMAC。Django 在每个请求中验证会话中的哈希是否与请求期间计算的哈希匹配。这允许用户通过更改密码来注销他们的所有会话。
Django 默认包含的密码更改视图,包括 PasswordChangeView
和 django.contrib.auth
管理界面中的 user_change_password
视图,会使用新的密码哈希更新会话,以便用户更改自己的密码不会注销自己。如果你有自定义的密码更改视图并希望具有类似的行为,可以使用 update_session_auth_hash()
函数。
-
aupdate_session_auth_hash
(request, user)¶ 异步版本:
aupdate_session_auth_hash()
这个函数接受当前请求和已更新的用户对象,新的会话哈希将从中派生,并适当地更新会话哈希。它还会轮换会话密钥,以使被盗的会话 cookie 无效。
用法示例:
from django.contrib.auth import update_session_auth_hash def password_change(request): if request.method == "POST": form = PasswordChangeForm(user=request.user, data=request.POST) if form.is_valid(): form.save() update_session_auth_hash(request, form.user) else: ...
Changed in Django 5.0:aupdate_session_auth_hash()
函数已添加。
备注
由于 get_session_auth_hash()
基于 SECRET_KEY
,在更新站点以使用新的密钥时,必须轮换密钥值,以避免使现有会话失效。有关详细信息,请参阅 SECRET_KEY_FALLBACKS
。
认证视图¶
Django 提供了多个视图,你可以用来处理登录、注销和密码管理。这些视图使用了 内置的身份验证表单,但你也可以传入自己的表单。
Django 不提供身份验证视图的默认模板。你应该为想要使用的视图创建自己的模板。模板上下文在每个视图中都有文档记录,详见 所有认证视图。
使用视图¶
有多种方法可以在项目中实现这些视图。最简单的方法是在你自己的 URL 配置中包含提供的 URLconf,例如:
urlpatterns = [
path("accounts/", include("django.contrib.auth.urls")),
]
这将包括以下 URL 模式:
accounts/login/ [name='login']
accounts/logout/ [name='logout']
accounts/password_change/ [name='password_change']
accounts/password_change/done/ [name='password_change_done']
accounts/password_reset/ [name='password_reset']
accounts/password_reset/done/ [name='password_reset_done']
accounts/reset/<uidb64>/<token>/ [name='password_reset_confirm']
accounts/reset/done/ [name='password_reset_complete']
这些视图提供了一个URL名称,以便更容易引用。有关使用命名URL模式的详细信息,请参阅 URL文档。
如果你想要更多控制你的 URL,你可以在你的 URLconf 中引用一个特定的视图:
from django.contrib.auth import views as auth_views
urlpatterns = [
path("change-password/", auth_views.PasswordChangeView.as_view()),
]
视图有可选参数,你可以使用这些参数来更改视图的行为。例如,如果你想要更改视图使用的模板名称,你可以提供 template_name
参数。一种方法是在 URLconf 中提供关键字参数,这些参数将传递给视图。例如:
urlpatterns = [
path(
"change-password/",
auth_views.PasswordChangeView.as_view(template_name="change-password.html"),
),
]
所有视图都是 基于类的,这使你可以通过子类化轻松自定义它们。
所有认证视图¶
这是所有由 django.contrib.auth
提供的视图列表。有关实现细节,请参阅 使用视图。
-
class
LoginView
[源代码]¶ URL 名称:
login
请参阅 URL 文档 以获取有关使用命名 URL 模式的详细信息。
方法和属性
-
template_name
¶ 用于显示用户登录视图的模板名称。默认为
registration/login.html
。
-
next_page
¶ 登录后重定向的 URL。默认为
LOGIN_REDIRECT_URL
。
-
redirect_field_name
¶ 包含登录后重定向 URL 的
GET
字段名称。默认为next
。如果传递了给定的GET
参数,将覆盖get_default_redirect_url()
URL。
-
authentication_form
¶ 用于身份验证的可调用对象(通常是一个表单类)。默认为
AuthenticationForm
。
-
extra_context
¶ 一个包含将添加到传递给模板的默认上下文数据的字典。
-
redirect_authenticated_user
¶ 一个布尔值,控制是否对已经认证的用户访问登录页面进行重定向,就好像他们刚刚成功登录一样。默认为
False
。警告
如果启用了
redirect_authenticated_user
,其他网站可以通过请求重定向到你网站上的图像文件的 URL 来确定他们的访问者是否在你的站点上进行了身份验证。为了避免这种 "社交媒体指纹识别" 信息泄漏,将所有图像和您的站点图标托管在一个单独的域上。启用
redirect_authenticated_user
在使用permission_required()
装饰器时可能会导致重定向循环,除非使用raise_exception
参数。
-
success_url_allowed_hosts
¶ 一个主机集合,用于登录后的重定向,除了
request.get_host()
,默认为空set
。
-
get_default_redirect_url
()[源代码]¶ 返回登录后重定向的URL。如果设置了默认实现会解析并返回
next_page
,否则返回LOGIN_REDIRECT_URL
。
以下是
LoginView
所做的事情:- 如果通过
GET
调用,它会显示一个登录表单,该表单将数据 POST 到相同的 URL。稍后会详细介绍这一点。 - 如果通过用户提交的凭据调用
POST
,它会尝试登录用户。如果登录成功,视图将重定向到next
参数指定的 URL。如果未提供next
,它将重定向到settings.LOGIN_REDIRECT_URL
(默认为/accounts/profile/
)。如果登录不成功,它会重新显示登录表单。
你需要提供登录模板的 HTML,默认情况下命名为
registration/login.html
。该模板将接收四个模板上下文变量:form
: 一个代表AuthenticationForm
的Form
对象。next
: 登录成功后要重定向的 URL。这可能包含查询字符串。site
: 根据SITE_ID
设置,表示当前的Site
。如果你没有安装站点框架,它将设置为RequestSite
的实例,该实例从当前的HttpRequest
获取站点名称和域名。site_name
: 用于别名site.name
。如果你没有安装站点框架,它将设置为request.META['SERVER_NAME']
的值。有关站点的更多信息,请参阅 “站点”框架。
如果你不想将模板命名为
registration/login.html
,你可以通过额外的参数将template_name
参数传递给 URLconf 中的as_view
方法。例如,以下 URLconf 行将使用myapp/login.html
:path("accounts/login/", auth_views.LoginView.as_view(template_name="myapp/login.html")),
你还可以使用
redirect_field_name
指定包含登录后重定向 URL 的GET
字段的名称。默认情况下,该字段称为next
。以下是一个示例
registration/login.html
模板,你可以将其用作起点。它假定你有一个定义了content
块的base.html
模板:{% extends "base.html" %} {% block content %} {% if form.errors %} <p>Your username and password didn't match. Please try again.</p> {% endif %} {% if next %} {% if user.is_authenticated %} <p>Your account doesn't have access to this page. To proceed, please login with an account that has access.</p> {% else %} <p>Please login to see this page.</p> {% endif %} {% endif %} <form method="post" action="{% url 'login' %}"> {% csrf_token %} <table> <tr> <td>{{ form.username.label_tag }}</td> <td>{{ form.username }}</td> </tr> <tr> <td>{{ form.password.label_tag }}</td> <td>{{ form.password }}</td> </tr> </table> <input type="submit" value="login"> <input type="hidden" name="next" value="{{ next }}"> </form> {# Assumes you set up the password_reset view in your URLconf #} <p><a href="{% url 'password_reset' %}">Lost password?</a></p> {% endblock %}
如果你已经自定义了身份验证(参见 自定义身份验证),你可以通过设置
authentication_form
属性来使用自定义的身份验证表单。这个表单必须在其__init__()
方法中接受一个request
关键字参数,并提供一个返回经过身份验证的用户对象的get_user()
方法(此方法仅在表单验证成功后调用)。-
-
class
LogoutView
[源代码]¶ 在
POST
请求中登出用户。URL 名称:
logout
属性:
-
next_page
¶ 登出后重定向的 URL。默认为
LOGOUT_REDIRECT_URL
。
-
template_name
¶ 在用户注销后显示的模板的全名。默认为
registration/logged_out.html
。
-
extra_context
¶ 一个包含将添加到传递给模板的默认上下文数据的字典。
-
success_url_allowed_hosts
¶ 一个
set
,除了request.get_host()
外,用于在注销后安全重定向的主机。默认为空的set
。
模板上下文:
title
:本地化的字符串 "已注销"。site
: 根据SITE_ID
设置,表示当前的Site
。如果你没有安装站点框架,它将设置为RequestSite
的实例,该实例从当前的HttpRequest
获取站点名称和域名。site_name
: 用于别名site.name
。如果你没有安装站点框架,它将设置为request.META['SERVER_NAME']
的值。有关站点的更多信息,请参阅 “站点”框架。
-
-
logout_then_login
(request, login_url=None)[源代码]¶ 在
POST
请求上注销用户,然后重定向到登录页面。URL 名称: 未提供默认 URL
可选参数:
login_url
: 要重定向到的登录页面的 URL。如果未提供,默认为settings.LOGIN_URL
。
-
class
PasswordChangeView
[源代码]¶ URL 名称:
password_change
允许用户更改他们的密码。
属性:
-
template_name
¶ 用于显示密码更改表单的模板的完整名称。如果未提供,默认为
registration/password_change_form.html
。
-
success_url
¶ 在成功更改密码后重定向到的 URL。默认为
'password_change_done'
。
-
form_class
¶ 一个自定义的“更改密码”表单,必须接受一个
user
关键字参数。该表单负责实际更改用户的密码。默认为PasswordChangeForm
。
-
extra_context
¶ 一个包含将添加到传递给模板的默认上下文数据的字典。
模板上下文:
form
:密码更改表单(参见上面的form_class
)。
-
-
class
PasswordChangeDoneView
[源代码]¶ URL 名称:
password_change_done
用户成功更改密码后显示的页面。
属性:
-
template_name
¶ 要使用的模板的完整名称。如果未提供,默认为
registration/password_change_done.html
。
-
extra_context
¶ 一个包含将添加到传递给模板的默认上下文数据的字典。
-
-
class
PasswordResetView
[源代码]¶ URL 名称:
password_reset
允许用户通过生成一次性使用的链接来重置密码,并将该链接发送到用户注册的电子邮件地址。
如果满足以下条件,此视图将发送电子邮件:
- 提供的电子邮件地址存在于系统中。
- 所请求的用户处于活动状态(
User.is_active
为True
)。 - 所请求的用户具有可用的密码。被标记为不可用密码的用户(参见
set_unusable_password()
)不允许请求密码重置,以防止在使用外部身份验证源如 LDAP 时被滥用。
如果不满足上述任何条件,将不会发送电子邮件,但用户也不会收到任何错误消息。这可以防止信息泄漏给潜在的攻击者。如果您想在这种情况下提供错误消息,可以子类化
PasswordResetForm
并使用form_class
属性。备注
请注意,发送电子邮件会耗费额外的时间,因此由于重置请求的持续时间与不存在的电子邮件地址的重置请求的持续时间之间存在差异,您可能会容易受到电子邮件地址枚举时序攻击的影响。为了减少开销,您可以使用第三方包,允许异步发送电子邮件,例如 django-mailer。
属性:
-
template_name
¶ 要用于显示密码重置表单的模板的完整名称。如果未提供,默认为
registration/password_reset_form.html
。
-
form_class
¶ 将用于获取要重置密码的用户电子邮件的表单。默认为
PasswordResetForm
。
-
email_template_name
¶ 要用于生成带有重置密码链接的电子邮件的模板的完整名称。如果未提供,默认为
registration/password_reset_email.html
。
-
subject_template_name
¶ 要用于电子邮件主题的模板的完整名称,其中包含重置密码链接。如果未提供,默认为
registration/password_reset_subject.txt
。
-
token_generator
¶ 用于检查一次性链接的类的实例。默认为
default_token_generator
,它是django.contrib.auth.tokens.PasswordResetTokenGenerator
的实例。
-
success_url
¶ 在成功重置密码请求后重定向到的 URL。默认为
'password_reset_done'
。
-
from_email
¶ 有效的电子邮件地址。默认情况下,Django 使用
DEFAULT_FROM_EMAIL
。
-
extra_context
¶ 一个包含将添加到传递给模板的默认上下文数据的字典。
-
html_email_template_name
¶ 用于生成带有密码重置链接的 text/html 多部分电子邮件的模板的完整名称。默认情况下,不会发送 HTML 电子邮件。
-
extra_email_context
¶ 一个包含在电子邮件模板中可用的上下文数据的字典。它可以用于覆盖下面列出的默认模板上下文值,例如
domain
。
模板上下文:
form
:用于重置用户密码的表单(参见上面的form_class
)。
电子邮件模板上下文:
email
:user.email
的别名user
:根据email
表单字段,表示当前的User
。只有活动用户才能重置他们的密码(User.is_active is True
)。site_name
: 用于别名site.name
。如果你没有安装站点框架,它将设置为request.META['SERVER_NAME']
的值。有关站点的更多信息,请参阅 “站点”框架。domain
:site.domain
的别名。如果你没有安装站点框架,这将设置为request.get_host()
的值。protocol
:http 或 httpsuid
:使用 base 64 编码过的用户主键。token
:检测重置密码链接是否有效的 Token 。
示例
registration/password_reset_email.html
(电子邮件正文模板):Someone asked for password reset for email {{ email }}. Follow the link below: {{ protocol}}://{{ domain }}{% url 'password_reset_confirm' uidb64=uid token=token %}
相同的模板上下文用于主题模板。主题必须是单行纯文本字符串。
-
class
PasswordResetDoneView
[源代码]¶ URL 名称:
password_reset_done
在向用户发送了重置密码链接后显示的页面。如果
PasswordResetView
没有显式设置success_url
URL,则默认调用此视图。备注
如果提供的电子邮件地址在系统中不存在,用户处于非活动状态,或者密码不可用,用户仍然会被重定向到此视图,但不会发送电子邮件。
属性:
-
template_name
¶ 要使用的模板的完整名称。如果未提供,默认为
registration/password_reset_done.html
。
-
extra_context
¶ 一个包含将添加到传递给模板的默认上下文数据的字典。
-
-
class
PasswordResetConfirmView
[源代码]¶ URL 名称:
password_reset_confirm
提供一个用于输入新密码的表单。
来自 URL 的关键字参数:
uidb64
:以 base 64 编码的用户 id。token
:检查密码是否有效的 Token。
属性:
-
template_name
¶ 用于显示确认密码视图的模板的完整名称。默认值为
registration/password_reset_confirm.html
。
-
token_generator
¶ 用于检查密码的类的实例。默认为
default_token_generator
,它是django.contrib.auth.tokens.PasswordResetTokenGenerator
的实例。
-
post_reset_login
¶ 一个布尔值,指示在成功重置密码后是否应自动对用户进行身份验证。默认为
False
。
-
post_reset_login_backend
¶ 如果
post_reset_login
为True
,在对用户进行身份验证时要使用的身份验证后端的点路径。只有在配置了多个AUTHENTICATION_BACKENDS
时才需要。默认为None
。
-
form_class
¶ 将用于设置密码的表单。默认为
SetPasswordForm
。
-
success_url
¶ 密码重置完成后重定向的 URL。默认为
'password_reset_complete'
。
-
extra_context
¶ 一个包含将添加到传递给模板的默认上下文数据的字典。
-
reset_url_token
¶ 作为密码重置 URL 的一部分显示的令牌参数。默认为
'set-password'
。
模板上下文:
form
:用于设置新用户密码的表单(参见上面的form_class
)。validlink
:布尔值,如果链接(uidb64
和token
的组合)有效或尚未使用,则为 True。
辅助函数¶
-
redirect_to_login
(next, login_url=None, redirect_field_name='next')[源代码]¶ 重定向到登录页面,登陆成功后跳转到其他 URL 。
必要参数
next
:成功登陆后跳转的 URL。
可选参数:
login_url
: 要重定向到的登录页面的 URL。如果未提供,默认为settings.LOGIN_URL
。redirect_field_name
:GET
字段包含的登录后跳转 URL 的参数名称。如果传递给定的GET
参数,将会覆盖next
。
内置表单¶
如果您不想使用内置视图,但希望方便地不必编写用于此功能的表单,身份验证系统提供了位于 django.contrib.auth.forms
中的几个内置表单:
备注
内置身份验证表单对其正在处理的用户模型进行了某些假设。如果您使用了 自定义用户模型,可能需要为身份验证系统定义自己的表单。有关更多信息,请参阅关于 在自定义用户模型中使用内置身份验证表单 的文档。
-
class
AdminPasswordChangeForm
[源代码]¶ A form used in the admin interface to change a user's password, including the ability to set an
unusable password
, which blocks the user from logging in with password-based authentication.将
user
作为第一个位置参数。Changed in Django 5.1:Option to disable (or reenable) password-based authentication was added.
-
class
AdminUserCreationForm
[源代码]¶ - New in Django 5.1.1.
A form used in the admin interface to create a new user. Inherits from
UserCreationForm
.It includes an additional
usable_password
field, enabled by default. Ifusable_password
is enabled, it verifies thatpassword1
andpassword2
are non empty and match, validates the password usingvalidate_password()
, and sets the user's password usingset_password()
. Ifusable_password
is disabled, no password validation is done, and password-based authentication is disabled for the user by callingset_unusable_password()
.
-
class
AuthenticationForm
[源代码]¶ 用于登录用户的表单。
将
request
作为其第一个位置参数,该参数存储在表单实例上,供子类使用。-
confirm_login_allowed
(user)[源代码]¶ 默认情况下,
AuthenticationForm
拒绝is_active
标志设置为False
的用户。您可以通过自定义策略来确定哪些用户可以登录来覆盖此行为。可以通过创建一个子类化AuthenticationForm
并覆盖confirm_login_allowed()
方法的自定义表单来实现这一点。如果给定的用户不允许登录,此方法应引发ValidationError
。例如,要允许所有用户登录,而不考虑其“active”状态:
from django.contrib.auth.forms import AuthenticationForm class AuthenticationFormWithInactiveUsersOkay(AuthenticationForm): def confirm_login_allowed(self, user): pass
(在这种情况下,您还需要使用允许不活动用户的身份验证后端,例如
AllowAllUsersModelBackend
。)或者只允许某些活动用户登录:
class PickyAuthenticationForm(AuthenticationForm): def confirm_login_allowed(self, user): if not user.is_active: raise ValidationError( _("This account is inactive."), code="inactive", ) if user.username.startswith("b"): raise ValidationError( _("Sorry, accounts starting with 'b' aren't welcome here."), code="no_b_users", )
-
-
class
BaseUserCreationForm
[源代码]¶ 用于创建新用户的
ModelForm
。如果您需要自定义用户创建表单,则推荐使用此基类。它有三个字段:
username
(来自用户模型)、password1
和password2
。它验证password1
和password2
是否匹配,使用validate_password()
验证密码,并使用set_password()
设置用户的密码。
-
class
PasswordResetForm
[源代码]¶ 用于生成并发送一次性使用链接以重置用户密码的表单。
-
send_mail
(subject_template_name, email_template_name, context, from_email, to_email, html_email_template_name=None)[源代码]¶ Uses the arguments to send an
EmailMultiAlternatives
. Can be overridden to customize how the email is sent to the user. If you choose to override this method, be mindful of handling potential exceptions raised due to email sending failures.参数: - subject_template_name -- 主题的模板。
- email_template_name -- 电子邮件正文的模板。
- context -- 传递给
subject_template
、email_template
和html_email_template
(如果不为None
)的上下文。 - from_email -- 发送者的电子邮件地址。
- to_email -- 请求者的电子邮件地址。
- html_email_template_name -- HTML 正文的模板;默认为
None
,在这种情况下发送纯文本电子邮件。
默认情况下,
save()
使用与PasswordResetView
传递给其电子邮件上下文的相同变量填充context
。
-
-
class
UserCreationForm
[源代码]¶ 继承自
BaseUserCreationForm
。为了防止与类似的用户名混淆,该表单不允许仅在大小写上不同的用户名。
模板中的认证数据¶
当您使用 RequestContext
时,当前已登录的用户及其权限将在 模板上下文 中提供。
技术细节
从技术上讲,只有在使用 RequestContext
并启用了 'django.contrib.auth.context_processors.auth'
上下文处理器时,这些变量才会在模板上下文中提供。它在默认生成的设置文件中已经存在。有关更多信息,请参阅 RequestContext 文档。
用户¶
在渲染模板时, RequestContext
会将当前已登录的用户,可以是 User
实例或 AnonymousUser
实例,存储在模板变量 {{ user }}
中:
{% if user.is_authenticated %}
<p>Welcome, {{ user.username }}. Thanks for logging in.</p>
{% else %}
<p>Welcome, new user. Please log in.</p>
{% endif %}
如果未使用 RequestContext
,则此模板上下文变量将不可用。
权限¶
当前已登录用户的权限存储在模板变量 {{ perms }}
中。这是 django.contrib.auth.context_processors.PermWrapper
的一个实例,它是权限的模板友好代理。
将 {{ perms }}
的单属性查找评估为布尔值是 User.has_module_perms()
的代理。例如,要检查已登录用户是否在 foo
应用程序中有任何权限:
{% if perms.foo %}
将双层属性查找评估为布尔值是 User.has_perm()
的代理。例如,要检查已登录用户是否具有权限 foo.add_vote
:
{% if perms.foo.add_vote %}
以下是在模板中检查权限的更完整的示例:
{% if perms.foo %}
<p>You have permission to do something in the foo app.</p>
{% if perms.foo.add_vote %}
<p>You can vote!</p>
{% endif %}
{% if perms.foo.add_driving %}
<p>You can drive!</p>
{% endif %}
{% else %}
<p>You don't have permission to do anything in the foo app.</p>
{% endif %}
也可以使用 {% if in %}
语句查找权限。例如:
{% if 'foo' in perms %}
{% if 'foo.add_vote' in perms %}
<p>In lookup works, too.</p>
{% endif %}
{% endif %}
在 admin 中管理用户¶
当您同时安装了 django.contrib.admin
和 django.contrib.auth
时,管理界面提供了一种方便的方式来查看和管理用户、组和权限。用户可以像任何 Django 模型一样创建和删除。可以创建组,并将权限分配给用户或组。还会存储和显示在管理界面内对模型的用户编辑记录。
创建用户¶
You should see a link to "Users" in the "Auth" section of the main admin index page. The "Add user" admin page is different than standard admin pages in that it requires you to choose a username and password before allowing you to edit the rest of the user's fields. Alternatively, on this page, you can choose a username and disable password-based authentication for the user.
还要注意:如果您希望用户帐户能够使用 Django 管理站点创建用户,您需要为其分配添加用户和更改用户(即“添加用户”和“更改用户”权限)。如果帐户有添加用户的权限但没有更改用户的权限,那么该帐户将无法添加用户。为什么?因为如果您有添加用户的权限,那么您就有了创建超级用户的权力,然后超级用户可以修改其他用户。因此,Django 要求同时具备添加和更改权限作为一种轻微的安全措施。
在允许用户管理权限时要谨慎。如果您授予非超级用户编辑用户的权限,这实际上与授予他们超级用户状态相同,因为他们将能够提升用户的权限,包括他们自己!
更改密码¶
User passwords are not displayed in the admin (nor stored in the database), but the password storage details are displayed. Included in the display of this information is a link to a password change form that allows admins to change or unset user passwords.