使用 Django 身份验证系统

本文档说明了 Django 身份验证系统在其默认配置中的用法。此配置已发展到可以满足最常见的项目需求,处理相当广泛的任务,并且对密码和权限进行了细致的实现。对于身份验证需求与默认设置不同的项目,Django 支持对身份验证进行广泛的扩展和自定义

Django 身份验证同时提供身份验证和授权,通常称为身份验证系统,因为这些功能在某种程度上是耦合的。

User 对象

User 对象是身份验证系统的核心。它们通常代表与你的网站进行交互的人,并且用于启用诸如限制访问、注册用户个人资料、将内容与创建者关联等功能。Django 的身份验证框架中只存在一类用户,即 '超级用户' 或管理员 '员工' 用户只是设置了特殊属性的用户对象,而不是不同类别的用户对象。

默认用户的基本属性是

请参阅 完整 API 文档 以获得完整参考,以下文档更面向任务。

创建用户

创建用户的最直接方式是使用包含的 create_user() 帮助程序函数

>>> from django.contrib.auth.models import User
>>> user = User.objects.create_user("john", "[email protected]", "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=[email protected]
...\> py manage.py createsuperuser --username=joe [email protected]

系统将提示您输入密码。输入密码后,将立即创建用户。如果您没有关闭 --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 还提供了 视图表单,可用于允许用户更改自己的密码。

更改用户的密码将注销其所有会话。有关详细信息,请参阅 密码更改时的会话失效

验证用户

authenticate(request=None, **credentials)
aauthenticate(request=None, **credentials)

异步版本: aauthenticate()

使用 authenticate() 验证一组凭证。它将凭证作为关键字参数,默认情况下为 usernamepassword,针对每个 验证后端 检查它们,如果凭证对后端有效,则返回 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

在 Django 5.0 中更改

添加了 aauthenticate() 函数。

权限和授权

Django 带有一个内置权限系统。它提供了一种向特定用户和用户组分配权限的方法。

Django 管理员站点使用它,但欢迎您在自己的代码中使用它。

Django 管理员站点使用权限如下

  • 查看对象的访问权限仅限于具有该类型对象的“查看”或“更改”权限的用户。
  • 查看“添加”表单并添加对象的访问权限仅限于具有该类型对象的“添加”权限的用户。
  • 查看更改列表、查看“更改”表单和更改对象的访问权限仅限于具有该类型对象的“更改”权限的用户。
  • 删除对象的访问权限仅限于具有该类型对象的“删除”权限的用户。

权限不仅可以针对每种类型的对象设置,还可以针对特定对象实例设置。通过使用 has_view_permission()has_add_permission()has_change_permission()has_delete_permission() 方法,由 ModelAdmin 类提供,可以自定义相同类型的不同对象实例的权限。

User 对象有两个多对多字段:groupsuser_permissionsUser 对象可以像访问任何其他 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()

默认权限

django.contrib.auth 在您的 INSTALLED_APPS 设置中列出时,它将确保为在您的已安装应用程序之一中定义的每个 Django 模型创建四个默认权限 – 添加、更改、删除和查看。

当您运行 manage.py migrate 时,将创建这些权限;在将 django.contrib.auth 添加到 INSTALLED_APPS 后首次运行 migrate 时,将为所有先前安装的模型以及当时安装的所有新模型创建默认权限。之后,每次运行 manage.py migrate 时,它都会为新模型创建默认权限(创建权限的功能连接到 post_migrate 信号)。

假设您有一个应用程序,其 app_labelfoo,模型名为 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,
)

然后可以通过 Useruser_permissions 属性或通过 Grouppermissions 属性将权限分配给 UserGroup

代理模型需要自己的内容类型

如果你想为 代理模型创建权限,请将 for_concrete_model=False 传递给 ContentTypeManager.get_for_model() 以获取适当的 ContentType

content_type = ContentType.objects.get_for_model(
    BlogPostProxy, for_concrete_model=False
)

权限缓存

首次需要获取ModelBackend权限检查后,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.
    ...
在 Django 5.0 中更改

添加了HttpRequest.auser()方法。

如何让用户登录

如果您有一个经过身份验证的用户想要附加到当前会话 - 这是通过login()函数完成的。

login(request, user, backend=None)
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.
        ...
在 Django 5.0 中更改

添加了 alogin() 函数。

选择认证后端

当用户登录时,用户的 ID 和用于认证的后端将保存在用户的会话中。这允许相同的 认证后端 在将来的请求中获取用户的详细信息。要保存在会话中的认证后端选择如下

  1. 如果提供了可选 backend 参数,请使用其值。
  2. 如果存在,请使用 user.backend 属性的值。这允许配对 authenticate()login()authenticate() 在它返回的用户对象上设置 user.backend 属性。
  3. 如果只有一个,请在 AUTHENTICATION_BACKENDS 中使用 backend
  4. 否则,引发异常。

在情况 1 和 2 中,backend 参数或 user.backend 属性的值应为带点的导入路径字符串(如 AUTHENTICATION_BACKENDS 中找到的那样),而不是实际的后端类。

如何注销用户

logout(request)
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() 时,当前请求的会话数据将被彻底清除。所有现有数据都将被删除。这是为了防止其他人使用同一 Web 浏览器登录并访问前一个用户的会话数据。如果您希望在用户注销后立即向会话中添加任何内容,请在调用 django.contrib.auth.logout() 之后 执行此操作。

在 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 和您的登录视图已正确关联。例如,使用默认值,将以下行添加到您的 URLconf

from django.contrib.auth import views as auth_views

path("accounts/login/", auth_views.LoginView.as_view()),

settings.LOGIN_URL 还接受视图函数名称和 命名的 URL 模式。这允许您在 URLconf 中自由地重新映射您的登录视图,而无需更新设置。

注意

login_required 装饰器不会检查用户上的 is_active 标志,但默认的 AUTHENTICATION_BACKENDS 拒绝不活跃的用户。

另请参阅

如果您正在为 Django 的管理编写自定义视图(或需要与内置视图使用的相同授权检查),则可能会发现 django.contrib.admin.views.decorators.staff_member_required() 装饰器是 login_required() 的有用替代方案。

LoginRequiredMixin 混合

使用 基于类的视图 时,可以通过使用 LoginRequiredMixin 来实现与 login_required 相同的行为。此混合应位于继承列表的最左侧。

class LoginRequiredMixin

如果视图使用此混合,则所有未经身份验证用户的请求都将根据 raise_exception 参数重定向到登录页面或显示 HTTP 403 禁止错误。

可以设置 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 拒绝非活动用户。

限制对通过测试的已登录用户的访问

要基于某些权限或其他测试限制访问,您需要执行与上一部分中描述的内容基本相同的操作。

可以在视图中直接对 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。它可能是一个登录页面,如果你未指定,则默认为 settings.LOGIN_URL
redirect_field_name
login_required() 相同。将其设置为 None 会从 URL 中将其移除,如果你将未通过测试的用户重定向到没有“下一页”的非登录页面,你可能需要这样做。

例如

@user_passes_test(email_check, login_url="/login/")
def my_view(request): ...
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)

检查用户是否具有特定权限是一项相对常见的任务。因此,Django 为这种情况提供了一个快捷方式:permission_required() 装饰器。

from django.contrib.auth.decorators import permission_required


@permission_required("polls.add_choice")
def my_view(request): ...

就像 has_perm() 方法一样,权限名称采用以下形式 "<app label>.<permission codename>"(即 polls.add_choice 表示 polls 应用程序中模型上的权限)。

装饰器还可以采用权限的迭代,在这种情况下,用户必须拥有所有权限才能访问视图。

请注意,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): ...

LoginViewredirect_authenticated_user=True 并且已登录用户不具有所有必需的权限时,这也避免了重定向循环。

PermissionRequiredMixin 混合

若要将权限检查应用于 基于类的视图,可以使用 PermissionRequiredMixin

class PermissionRequiredMixin

此 mixin 就像 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 的任何参数以自定义对未授权用户的处理。

你还可以覆盖这些方法

get_permission_required()

返回 mixin 使用的权限名称的迭代。默认为 permission_required 属性,必要时转换为元组。

has_permission()

返回一个布尔值,表示当前用户是否有权执行装饰的视图。默认情况下,这会返回调用 has_perms() 的结果,其中权限列表由 get_permission_required() 返回。

在基于类的视图中重定向未授权的请求

为了简化 基于类的视图 中的访问限制处理,AccessMixin 可用于在拒绝访问时配置视图的行为。已验证的用户将被拒绝访问,并收到 HTTP 403 Forbidden 响应。匿名用户将被重定向到登录页面或显示 HTTP 403 Forbidden 响应,具体取决于 raise_exception 属性。

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,则返回 login_url,否则返回 settings.LOGIN_URL

get_permission_denied_message()

raise_exceptionTrue 时,此方法可用于控制传递给错误处理程序以显示给用户的错误消息。默认情况下返回 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 附带的默认密码更改视图 PasswordChangeViewdjango.contrib.auth 管理中的 user_change_password 视图,会使用新密码哈希更新会话,以便更改自己密码的用户不会退出登录。如果您有自定义密码更改视图并希望有类似的行为,请使用 update_session_auth_hash() 函数。

update_session_auth_hash(request, user)
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:
        ...
在 Django 5.0 中更改

添加了 aupdate_session_auth_hash() 函数。

注意

由于 get_session_auth_hash() 基于 SECRET_KEY,因此在将您的网站更新为使用新密钥时,必须轮换密钥值以避免使现有会话失效。有关详细信息,请参阅 SECRET_KEY_FALLBACKS

身份验证视图

Django 提供了几个视图,您可以使用它们来处理登录、注销和密码管理。这些视图使用 内置身份验证表单,但您也可以传入您自己的表单。

Django 没有为身份验证视图提供默认模板。您应该为要使用的视图创建自己的模板。模板上下文在每个视图中都有记录,请参阅 所有身份验证视图

使用视图

有不同的方法可以在您的项目中实现这些视图。最简单的方法是在您自己的 URLconf 中包含 django.contrib.auth.urls 中提供的 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 来确定其访问者是否在您的网站上经过身份验证。为了避免这种“社交媒体指纹识别”信息泄露,请将所有图像和 favicon 托管在单独的域上。

启用 redirect_authenticated_user 还会在使用 permission_required() 装饰器时导致重定向循环,除非使用 raise_exception 参数。

success_url_allowed_hosts

除了 request.get_host() 之外的主机的一个 set,在登录后重定向时是安全的。默认为一个空的 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:表示 AuthenticationFormForm 对象。
  • next:成功登录后要重定向到的 URL。它可能也包含一个查询字符串。
  • 站点:当前 站点,根据 SITE_ID 设置。如果您未安装站点框架,这将设置为 RequestSite 的实例,该实例从当前 HttpRequest 派生站点名称和域名。
  • 站点名称site.name 的别名。如果您未安装站点框架,这将设置为 request.META['SERVER_NAME'] 的值。有关站点的更多信息,请参见 “站点”框架

如果您不想调用模板 registration/login.html,您可以通过 as_view 方法中的额外参数传递 template_name 参数。例如,此 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() 方法,该方法返回经过身份验证的用户对象(此方法仅在表单验证成功后调用)。

LogoutView

POST 请求上注销用户。

URL 名称: logout

属性

next_page

注销后要重定向到的 URL。默认为 LOGOUT_REDIRECT_URL

template_name

注销用户后要显示的模板的全名。默认为 registration/logged_out.html

redirect_field_name

包含注销后要重定向到的 URL 的 GET 字段的名称。默认为 'next'。如果传递了给定的 GET 参数,则会覆盖 next_page URL。

extra_context

将添加到传递给模板的默认上下文数据中的上下文数据字典。

success_url_allowed_hosts

除了 request.get_host() 之外,还可以安全重定向到的一组主机,默认为一个空 set

模板上下文

  • title:已本地化的字符串“已注销”。
  • 站点:当前 站点,根据 SITE_ID 设置。如果您未安装站点框架,这将设置为 RequestSite 的实例,该实例从当前 HttpRequest 派生站点名称和域名。
  • 站点名称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_activeTrue)。
  • 请求的用户具有可用的密码。使用不可用密码标记的用户(请参阅 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)。

电子邮件模板上下文

  • emailuser.email 的别名
  • user:根据 email 表单字段,当前的 User。只有活动用户才能重置其密码(User.is_active is True)。
  • 站点名称site.name 的别名。如果您未安装站点框架,这将设置为 request.META['SERVER_NAME'] 的值。有关站点的更多信息,请参见 “站点”框架
  • domainsite.domain 的别名。如果您没有安装站点框架,则将其设置为 request.get_host() 的值。
  • protocol:http 或 https
  • uid:以 base 64 编码的用户的主键。
  • 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:用于检查密码是否有效的令牌。

属性

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_loginTrue,则用于对用户进行身份验证时,对身份验证后端进行点状路径访问。仅在配置了多个 AUTHENTICATION_BACKENDS 时才需要。默认为 None

form_class

将用于设置密码的表单。默认为 SetPasswordForm

success_url

密码重置完成后重定向到的 URL。默认为 'password_reset_complete'

extra_context

将添加到传递给模板的默认上下文数据中的上下文数据字典。

reset_url_token

显示为密码重置 URL 组件的令牌参数。默认为 'set-password'

模板上下文

  • form:用于设置新用户密码的表单(参见上文的 form_class)。
  • validlink:布尔值,如果链接(uidb64token 的组合)有效或尚未使用,则为 True。
class PasswordResetCompleteView

URL 名称: password_reset_complete

显示一个视图,告知用户密码已成功更改。

属性

template_name

用于显示视图的模板的全名。默认为 registration/password_reset_complete.html

extra_context

将添加到传递给模板的默认上下文数据中的上下文数据字典。

辅助函数

redirect_to_login(next, login_url=None, redirect_field_name='next')

重定向到登录页面,然后在成功登录后返回到另一个 URL。

必需参数

  • next:成功登录后要重定向到的 URL。

可选参数

  • login_url:要重定向到的登录页面的 URL。如果未提供,则默认为 settings.LOGIN_URL
  • redirect_field_name:包含登录后要重定向到的 URL 的 GET 字段的名称。如果传递了给定的 GET 参数,则会覆盖 next

内置表单

如果你不想使用内置视图,但又想方便地不必为该功能编写表单,则身份验证系统会提供几个位于 django.contrib.auth.forms 中的内置表单。

注意

内置身份验证表单对它们所使用的用户模型做出了一些假设。如果你正在使用 自定义用户模型,则可能需要为身份验证系统定义你自己的表单。有关更多信息,请参阅有关 将内置身份验证表单与自定义用户模型一起使用 的文档。

class AdminPasswordChangeForm

在管理界面中用于更改用户密码的表单。

user 作为第一个位置参数。

class AuthenticationForm

用于让用户登录的表单。

request 作为其第一个位置参数,该参数存储在表单实例中供子类使用。

confirm_login_allowed(user)

默认情况下,AuthenticationForm 拒绝其 is_active 标记设置为 False 的用户。您可以使用自定义策略覆盖此行为,以确定哪些用户可以登录。使用继承 AuthenticationForm 并覆盖 confirm_login_allowed() 方法的自定义表单来执行此操作。如果给定的用户无法登录,此方法应引发 ValidationError

例如,允许所有用户登录,而不管其“活动”状态如何

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 PasswordChangeForm

允许用户更改其密码的表单。

class PasswordResetForm

用于生成并通过电子邮件发送一次性链接以重置用户密码的表单。

send_mail(subject_template_name, email_template_name, context, from_email, to_email, html_email_template_name=None)

使用参数发送 EmailMultiAlternatives。可以覆盖它以自定义向用户发送电子邮件的方式。

参数
  • subject_template_name – 主题模板。
  • email_template_name – 邮件正文模板。
  • context – 传递给 subject_templateemail_templatehtml_email_template(如果它不是 None)的上下文。
  • from_email – 发件人的电子邮件。
  • to_email – 请求者的电子邮件。
  • html_email_template_name – HTML 正文模板;默认为 None,在这种情况下,将发送纯文本电子邮件。

默认情况下,save()PasswordResetView 传递给其电子邮件上下文的相同变量填充 context

class SetPasswordForm

一种允许用户在不输入旧密码的情况下更改其密码的表单。

class UserChangeForm

在管理界面中用于更改用户信息和权限的表单。

class BaseUserCreationForm
Django 4.2 中的新增功能。

用于创建新用户的 ModelForm。如果您需要自定义用户创建表单,这是推荐的基本类。

它有三个字段:username(来自用户模型)、password1password2。它验证 password1password2 是否匹配,使用 validate_password() 验证密码,并使用 set_password() 设置用户的密码。

UserCreationForm

继承自 BaseUserCreationForm。为了帮助避免与类似用户名混淆,该表单不允许仅在大小写上有所不同的用户名。

在 Django 4.2 中更改

在较早版本中,UserCreationForm 不会保存自定义用户模型的许多对多表单字段。

在较早版本中,允许仅在大小写上有所不同的用户名。

模板中的身份验证数据

当你使用 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 %}

在管理中管理用户

当同时安装了 django.contrib.admindjango.contrib.auth 时,管理提供了一种方便的方法来查看和管理用户、组和权限。用户可以像任何 Django 模型一样创建和删除。可以创建组,并且可以将权限分配给用户或组。还在管理中存储和显示用户对模型的编辑日志。

创建用户

你应该在主管理索引页面的“Auth”部分中看到一个指向“Users”的链接。“添加用户”管理页面不同于标准管理页面,因为它要求你在允许你编辑用户其余字段之前选择一个用户名和密码。

另请注意:如果你希望用户帐户能够使用 Django 管理网站创建用户,则需要授予他们添加用户更改用户的权限(即“添加用户”和“更改用户”权限)。如果一个帐户有权添加用户但无权更改用户,则该帐户将无法添加用户。为什么?因为如果你有权添加用户,则你有权创建超级用户,然后超级用户又可以更改其他用户。因此,Django 要求添加更改权限作为轻微的安全措施。

要深思熟虑地允许用户管理权限。如果你授予非超级用户编辑用户的权限,这最终等同于授予他们超级用户状态,因为他们将能够提升包括他们自己在内的用户的权限!

更改密码

用户密码不会显示在管理员中(也不会存储在数据库中),但会显示密码存储详细信息。显示此信息时会包含一个密码更改表单的链接,允许管理员更改用户密码。

返回顶部