Django 亮点:用户模型和身份验证(第 1 部分)
已发表: 2022-03-10有两种类型的网站:静态和动态。 Django 是一个开发动态网站的框架。 虽然静态网站是一个仅提供信息的网站,但没有任何交互(除了简单的页面请求)会注册到服务器。 在静态网站中,服务器将 HTML、CSS 和 JavaScript 发送到客户端,仅此而已。 更多功能需要动态网站,其中服务器存储信息并响应用户交互,而不仅仅是提供页面。 开发动态站点的一个主要原因是对用户进行身份验证并限制内容。
编写、部署和管理静态网站比动态网站更容易、更便宜、更安全。 因此,仅当您的项目需要动态范例的附加功能时,您才应该创建一个动态网站。 Django 使用其内置组件简化并简化了创建动态站点的过程。 作为动态 Web 应用程序的主要组件之一,“用户帐户”对象,如轮子,很容易重新发明,但标准形状适用于大多数用途。 Django 提供了一个强大的开箱即用的用户模型,在本文中,我们将介绍提供安全、直观的用户身份验证流程的最佳方法。
系列中的其他部分:
- Django 亮点:模板化保存行(第 2 部分)
- Django 亮点:模型、管理和利用关系数据库(第 3 部分)
- Django 亮点:处理静态资产和媒体文件(第 4 部分)
开始设置
如果您想创建自己的 Django 应用程序来试验本文中的概念,您可以创建一个目录(最好是虚拟环境),然后运行以下命令:
pip install django django-admin startproject PROJECTNAME cd PROJECTNAME python manage.py startapp APPNAME python manage.py migrate python manage.py runserver
如果您正在寻找创建您的第一个 Django 项目的演练,Django 自己的网站提供了一个很好的。 在本文中,我们没有使用示例项目,但所讨论的概念将适用于几乎所有 Django 应用程序。
标准用户模型
从根本上说,用户帐户的概念存在两个原因:访问控制和个性化应用程序状态。 访问控制是指系统上的资源仅对某些用户可用。 个性化状态很大程度上取决于应用程序的用途,但可能包括设置、数据或任何其他特定于单个用户的记录。 Django stock User
模型为这两个用例提供了合理的方法。
最初,Django 应用程序中有两种类型的用户:超级用户帐户和普通用户。 超级用户拥有普通帐户的所有属性和权限,还可以访问 Django 管理面板,这是一个强大的应用程序,我们将在以后的文章中详细探讨。 本质上,超级用户可以创建、编辑或删除应用程序中的任何数据,包括其他用户帐户。
从根本上说,用户帐户的概念存在两个原因:访问控制和个性化应用程序状态。
“
要在您自己的 Django 应用程序上创建超级用户,请运行:
python manage.py createsuperuser
用户帐户的另一个好处是将个性化数据存储到数据库中。 默认情况下,Django 只需要用户名和密码,但提供可选字段供用户输入他们的名字、姓氏和电子邮件地址。 您可以在 Django 网站上阅读完整的模型参考。 我们将在下面讨论扩展这个模型。
安全性和可靠性
Django 在用户模型中包含大量的密码管理中间件。 用户密码必须至少为 8 个字符,不能全部为数字,不能与用户名匹配太近,并且不在 20,000 个最常用密码的列表中。 Django 库存表格验证了这些要求。 当密码发送到服务器时,它会在存储之前进行加密,默认情况下使用带有 SHA256 哈希的 PBKDF2 算法。 总体而言,默认密码系统无需开发人员的任何努力即可提供强大的安全性。 除非您有特定的专业知识和令人信服的理由来更改应用程序中处理密码的方式,否则不要修改此行为。
另一个方便的内置功能是要求用户名是唯一的。 如果您考虑一下,如果有两个用户名为“djangofan1”的用户,并且服务器收到该用户名的登录请求,它将不知道哪个用户正在尝试访问该应用程序。 对他们来说不幸的是,第二个尝试注册“djangofan1”的用户将不得不选择一个不同的名称,也许是“djangofan2”。 这种唯一性约束在数据库级别强制执行,但再次由 Django 提供的表单验证。
最后,关于删除用户的说明。 虽然删除用户是可能的,但大多数复杂的应用程序都会有许多资源与每个用户帐户相关联。 如果您想在不删除那些附加对象的情况下有效地删除用户,请将用户的is_active
字段设置为 false。 然后,那些其他资源仍然与帐户相关联,而不是被自己删除,并且超级用户可以随时重新激活用户帐户。 请注意,这不会释放用户名,但将帐户设置为非活动状态以及将用户名更改为随机的唯一值可以达到相同的效果。 最后,如果您的站点政策或适用的当地法律要求用户帐户是完全可删除的,那么is_active
方法是不够的。
除非您有特定的专业知识和令人信服的理由来更改应用程序中处理密码的方式,否则不要修改此行为。
“
标准使用
当您想注册一个新的用户帐户时,这样做的视图可能类似于views.py中的以下内容:
from django.shortcuts import render, redirect from django.contrib.auth import authenticate, login from django.contrib.auth.forms import UserCreationForm def signup(request): if request.user.is_authenticated: return redirect('/') if request.method == 'POST': form = UserCreationForm(request.POST) if form.is_valid(): form.save() username = form.cleaned_data.get('username') password = form.cleaned_data.get('password1') user = authenticate(username=username, password=password) login(request, user) return redirect('/') else: return render(request, 'signup.html', {'form': form}) else: form = UserCreationForm() return render(request, 'signup.html', {'form': form})
让我们分解一下:
- 如果用户已经登录,我们会将他们从注册页面重定向。
- 如果请求方式是POST,则表示创建用户的表单已经填写完毕,可以创建用户了。
- 首先,使用用户提供的数据在后端构造表单对象。
- 如果表单有效,则创建用户并登录,然后将其发送到主页。
- 否则,将它们转储回用户创建页面,并提供有关哪些数据无效的信息(例如,它们请求的用户名已在使用中)。
- 否则,用户是第一次访问该页面,应该会看到创建新帐户的表单。
现在检查帐户登录:
from django.shortcuts import render, redirect from django.contrib.auth import authenticate, login from django.contrib.auth.forms import AuthenticationForm def signin(request): if request.user.is_authenticated: return render(request, 'homepage.html') if request.method == 'POST': username = request.POST['username'] password = request.POST['password'] user = authenticate(request, username=username, password=password) if user is not None: login(request, user) return redirect('/') else: form = AuthenticationForm(request.POST) return render(request, 'signin.html', {'form': form}) else: form = AuthenticationForm() return render(request, 'signin.html', {'form': form})
另一个细分:
- 如果用户已经登录,我们会将他们从登录页面重定向。
- 如果请求方法是 POST,则表示登录表单已填写,是时候对用户进行帐户身份验证了。
- 首先,使用用户提供的数据对用户进行身份验证
- 如果用户名和密码对应一个帐户,则将该用户登录
- 否则,将他们带回登录页面并预先填写表单信息
- 否则,用户是第一次访问该页面,应该会看到用于登录的表单。
最后,您的用户可能最终想要退出。 这个请求的基本代码很简单:
from django.shortcuts import render, redirect from django.contrib.auth import logout def signout(request): logout(request) return redirect('/')
一旦用户登录到他们的帐户,直到他们在该设备上注销,他们就拥有了一个“会话”。 在此期间,来自其浏览器的后续请求将能够访问仅限帐户的页面。 一个用户可以同时激活多个会话。 默认情况下,会话不会超时。
当用户在其设备上具有活动会话时,他们将注册为True
以进行request.user.is_authenticated
检查。 将页面限制为仅登录用户的另一种方法是函数上方的@login_required
装饰器。 有多种其他方法可以实现相同的目标,详见此处。
如果此级别的配置超出您的预期,则可以使用更加开箱即用的用户管理方法。 这些常用的身份验证视图为用户管理提供标准路由、视图和表单,并且可以通过将它们分配给自定义 URL、传递自定义模板甚至子类化视图来进行修改以进行更多控制。
扩展用户模型
通常,您需要扩展用户模型以提供更细粒度的访问控制或为每个帐户存储更多用户数据。 我们将在下面探讨几种常见情况。
从用户模型中删除字段
与本节的标题相反,我实际上不建议直接更改用户模型或关联的数据库模式! 在设置新 Django 项目期间,默认情况下会在您的数据库中建立通用用户模型。 默认用户模型与默认用户模型密切相关,因此更改它可能会在您的应用程序中产生意想不到的影响(尤其是如果您使用第三方库),因此不建议添加或删除字段,而且框架也不容易做到这一点。
默认情况下,用户唯一需要的两个字段是用户名和密码。 如果您不想使用任何其他字段,只需忽略它们的存在,因为可以创建没有名字、姓氏或电子邮件地址的用户。 有一组默认字段,如 last_login 和 date_joined,如果您不想要它们,也可以忽略它们。 如果您有需要从用户模型中删除可选字段的实际技术限制,那么您已经知道它并且不需要本文。
您可能希望在用户模型中删除字段的一个常见原因是删除用户名以支持电子邮件作为唯一标识符。 在这种情况下,当从表单数据创建用户或验证请求时,除了在电子邮件字段中使用之外,只需输入电子邮件地址作为用户名。 当用户名被格式化为电子邮件地址时,用户名字段仍将强制执行唯一性约束。 字符串是字符串,它们将被同等对待。
与默认用户模型有太多关联,因此更改它可能会对您的应用程序产生意想不到的影响,尤其是在您使用第三方库的情况下。
“
使用配置文件添加字段
同样,如果您想存储有关用户的额外信息,则不应尝试修改默认用户模型。 即使对于单个字段,也可以通过与现有用户的一对一关系定义您自己的对象。 这个额外的模型通常称为Profile
。 假设您想为每个用户存储中间名和出生日期 (dob)。 Profile
将在models.py中定义如下:
from django.db import models from django.contrib.auth.models import User class Profile(models.Model): user = models.OneToOneField(User, on_delete=models.CASCADE) middle_name = models.CharField(max_length=30, blank=True) dob = models.DateField(null=True, blank=True)
自定义访问控制
您的应用程序可能需要区分不同类型的用户来访问信息和功能。 Django 提供了一个用于创建自定义细粒度访问控制的系统:权限和组。
权限是确定对资源的访问权限的对象。 一个用户可以拥有一个或多个权限。 例如,他们可能具有对产品表的读取权限和对客户表的写入权限。 权限的确切实现因应用程序而异,但 Django 的方法可以直观地定义数据权限并将这些权限分配给用户。
企业和其他大型组织的应用程序通常实施基于角色的访问控制。 本质上,一个用户可以有不同的角色,每个角色都有一定的权限。 Django 实现这种模式的工具是 Group。 一个组可以有任意数量的权限,每个权限可以分配给任意数量的组。 然后,用户不是直接获得权限,而是通过他们的组成员资格获得权限,从而使应用程序更易于管理。
概述:集成支付提供商
集成支付处理器的精确过程因应用程序和提供商而异。 但是,我将笼统地介绍该过程。
外包支付的主要优势之一是提供商负责存储和验证高度监管的数据,例如信用卡信息。 然后,该服务与您的应用程序共享一些唯一的用户令牌以及有关其支付历史的数据。 与添加配置文件一样,您可以创建与核心User
关联的模型并将数据存储在该模型中。 与密码一样,重要的是要遵循与提供商的给定集成,以避免不正确地存储敏感信息。
概述:社交登录
我想简要介绍的另一个广泛而复杂的领域是社交登录。 Facebook 和 Google 等大型平台以及 GitHub 等小型网站都提供 API 以使用其服务对用户进行身份验证。 与支付提供商类似,这会在您的数据库中创建一条记录,将帐户链接到其数据库中的帐户。
您可能希望在您的网站中包含社交身份验证,以便用户更轻松地注册,而无需创建一组新的登录凭据。 如果您的应用程序仅针对已经使用提供社交身份验证选项的特定网站的客户,它可能会通过降低进入门槛来帮助您从该市场吸引用户。 此外,如果您的应用程序打算从第三方服务访问用户的数据,则在身份验证期间链接帐户可以简化授予权限的过程。
但是,使用社交身份验证也有缺点。 第三方提供商的 API 更改或中断可能会中断您的应用程序的正常运行时间或开发活动。 通常,外部依赖项会增加应用程序的复杂性。 最后,值得考虑您正在与之集成的第三方的数据收集和使用政策,并确保它们符合您和您的用户的期望。
如果您确实确定社交身份验证适合您的应用程序,幸运的是,有一个库可以满足您的要求。 Python Social Auth for Django 是一个包,用于在 Django 项目中启用 Python 的社交身份验证生态系统的功能。
包起来
与用户帐户一样普遍,它的广泛使用可能导致不必要地解决相同的问题。 希望本文向您展示了 Django 中可用的强大功能的范围,并让您了解创建用户帐户时应用程序的基本职责。
Django Highlights 是一个介绍 Django 中 Web 开发的重要概念的系列。 每篇文章都是作为 Django 开发方面的独立指南编写的,旨在帮助前端开发人员和设计人员更深入地了解代码库的“另一半”。 这些文章主要是为了帮助您了解理论和约定,但包含一些代码示例,这些示例是用 Django 3.0 编写的。 我们在本文中涉及的几个概念,包括模板、管理员用户、模型和表单,将在本系列的后续文章中单独详细探讨。
系列中的其他部分:
- Django 亮点:模板化保存行(第 2 部分)
- Django 亮点:模型、管理和利用关系数据库(第 3 部分)
- Django 亮点:处理静态资产和媒体文件(第 4 部分)