{"title":"Django中的密码管理","version":"6.1","locale":"zh-hans","docname":"topics/auth/passwords","url":"/zh-hans/6.1/topics/auth/passwords/","canonical":"https://djangodocs.dev/zh-hans/6.1/topics/auth/passwords/","summary":"密码管理通常不应该被重新再设计，Django 努力提供了一个安全且灵活的管理用户密码的工具。这篇文档描述了 Django 如何存储密码，如何配置存储哈希，和一些使用哈希密码的工具。 See also 即使用户使用了很强壮的密码，攻击者还是可以窃听他们的网络链接。用户使用 HTTPS 可以避免通过纯 HTTP…","html":"<h1>Django中的密码管理<a class=\"heading-anchor\" href=\"#password-management-in-django\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h1>\n<p>密码管理通常不应该被重新再设计，Django 努力提供了一个安全且灵活的管理用户密码的工具。这篇文档描述了 Django 如何存储密码，如何配置存储哈希，和一些使用哈希密码的工具。</p>\n<aside class=\"admonition admonition-seealso\">\n<p class=\"admonition-title\">See also</p>\n<p>即使用户使用了很强壮的密码，攻击者还是可以窃听他们的网络链接。用户使用 <a class=\"reference internal\" href=\"/zh-hans/6.1/topics/security/#security-recommendation-ssl\"><span class=\"std std-ref\">HTTPS</span></a> 可以避免通过纯 HTTP 链接发送密码（或其他一些敏感数据），因为它们很容易被密码嗅探。</p>\n</aside>\n<section id=\"how-django-stores-passwords\">\n<span id=\"auth-password-storage\"></span><h2>Django 如何存储密码<a class=\"heading-anchor\" href=\"#how-django-stores-passwords\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>Django 提供灵活的密码存储系统，默认使用 PBKDF2。</p>\n<p><a class=\"reference internal\" href=\"/zh-hans/6.1/ref/contrib/auth/#django.contrib.auth.models.User\" title=\"django.contrib.auth.models.User\"><code class=\"xref py py-class docutils literal notranslate\"><span class=\"pre\">User</span></code></a> 对象的 <a class=\"reference internal\" href=\"/zh-hans/6.1/ref/contrib/auth/#django.contrib.auth.models.User.password\" title=\"django.contrib.auth.models.User.password\"><code class=\"xref py py-attr docutils literal notranslate\"><span class=\"pre\">password</span></code></a> 属性是一个以以下格式的字符串：</p>\n<div class=\"code-block\" data-language=\"text\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Text</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Text code\"><code>&lt;algorithm&gt;$&lt;iterations&gt;$&lt;salt&gt;$&lt;hash&gt;\n</code></pre></div>\n<p>Those are the components used for storing a User's password, separated by the\ndollar-sign character and consist of: the hashing algorithm, the number of\nalgorithm iterations (work factor), the random salt, and the resulting password\nhash. The algorithm is one of a number of one-way hashing or password storage\nalgorithms Django can use; see below. Iterations describe the number of times\nthe algorithm is run over the hash. Salt is the random seed used and the hash\nis the result of the one-way function.</p>\n<p>默认情况下，Django 使用带有 SHA256 哈希的 <a class=\"reference external\" href=\"https://en.wikipedia.org/wiki/PBKDF2\">PBKDF2</a> 算法，它是 <a class=\"reference external\" href=\"https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-132.pdf\">NIST</a> 推荐的密码延展机制。它足够安全，需要大量的运算时间才能破解，这对大部分用户来说足够了。</p>\n<p>However, depending on your requirements, you may choose a different\nalgorithm, or even use a custom algorithm to match your specific\nsecurity situation. Again, most users shouldn't need to do this -- if\nyou're not sure, you probably don't. If you do, please read on:</p>\n<p>Django 通过查阅 <a class=\"reference internal\" href=\"/zh-hans/6.1/ref/settings/#std-setting-PASSWORD_HASHERS\"><code class=\"xref std std-setting docutils literal notranslate\"><span class=\"pre\">PASSWORD_HASHERS</span></code></a> 设置来选择要使用的算法。这是一个包含此 Django 安装支持的哈希算法类的列表。</p>\n<p>对于存储密码，Django 将使用 <a class=\"reference internal\" href=\"/zh-hans/6.1/ref/settings/#std-setting-PASSWORD_HASHERS\"><code class=\"xref std std-setting docutils literal notranslate\"><span class=\"pre\">PASSWORD_HASHERS</span></code></a> 中的第一个哈希算法。要使用不同的算法存储新密码，请将您首选的算法放在 <a class=\"reference internal\" href=\"/zh-hans/6.1/ref/settings/#std-setting-PASSWORD_HASHERS\"><code class=\"xref std std-setting docutils literal notranslate\"><span class=\"pre\">PASSWORD_HASHERS</span></code></a> 中的第一位。</p>\n<p>对于验证密码，Django 将在列表中查找与存储的密码中的算法名称匹配的哈希算法。如果存储的密码命名了在 <a class=\"reference internal\" href=\"/zh-hans/6.1/ref/settings/#std-setting-PASSWORD_HASHERS\"><code class=\"xref std std-setting docutils literal notranslate\"><span class=\"pre\">PASSWORD_HASHERS</span></code></a> 中找不到的算法，试图验证它将引发 <code class=\"docutils literal notranslate\"><span class=\"pre\">ValueError</span></code>。</p>\n<p><a class=\"reference internal\" href=\"/zh-hans/6.1/ref/settings/#std-setting-PASSWORD_HASHERS\"><code class=\"xref std std-setting docutils literal notranslate\"><span class=\"pre\">PASSWORD_HASHERS</span></code></a> 的默认值是：</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"n\">PASSWORD_HASHERS</span> <span class=\"o\">=</span> <span class=\"p\">[</span>\n    <span class=\"s2\">&quot;django.contrib.auth.hashers.PBKDF2PasswordHasher&quot;</span><span class=\"p\">,</span>\n    <span class=\"s2\">&quot;django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher&quot;</span><span class=\"p\">,</span>\n    <span class=\"s2\">&quot;django.contrib.auth.hashers.Argon2PasswordHasher&quot;</span><span class=\"p\">,</span>\n    <span class=\"s2\">&quot;django.contrib.auth.hashers.BCryptSHA256PasswordHasher&quot;</span><span class=\"p\">,</span>\n    <span class=\"s2\">&quot;django.contrib.auth.hashers.ScryptPasswordHasher&quot;</span><span class=\"p\">,</span>\n<span class=\"p\">]</span>\n</code></pre></div>\n<p>这意味着 Django 除了使用 <a class=\"reference external\" href=\"https://en.wikipedia.org/wiki/PBKDF2\">PBKDF2</a> 来存储所有密码，也支持使用 PBKDF2SHA1 、<a class=\"reference external\" href=\"https://en.wikipedia.org/wiki/Argon2\">argon2</a> 和 <a class=\"reference external\" href=\"https://en.wikipedia.org/wiki/Bcrypt\">bcrypt</a> 来检测已存储的密码。</p>\n<p>接下来的部分描述了高级用户修改这个配置的几个常见方法。</p>\n<section id=\"using-argon2-with-django\">\n<span id=\"argon2-usage\"></span><h3>在 Django 中使用 Argon2<a class=\"heading-anchor\" href=\"#using-argon2-with-django\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p><a class=\"reference external\" href=\"https://en.wikipedia.org/wiki/Argon2\">Argon2</a> 是 2015 年 <a class=\"reference external\" href=\"https://www.password-hashing.net/\">Password Hashing Competition</a> 的获胜者，这是一个由社区组织的开放式竞赛，旨在选择下一代哈希算法。它的设计目标是在自定义硬件上计算不应该比在普通 CPU 上计算容易。Argon2 密码哈希器的默认变种是 Argon2id。</p>\n<p><a class=\"reference external\" href=\"https://en.wikipedia.org/wiki/Argon2\">Argon2</a> 并不是 Django 的默认首选，因为它依赖第三方库。尽管哈希密码竞赛主办方建议立即使用 Argon2 ，而不是 Django 提供的其他算法。</p>\n<p>要将 Argon2id 用作默认的存储算法，请执行以下操作：</p>\n<ol class=\"arabic\">\n<li><p>安装 <a class=\"extlink-pypi reference external\" href=\"https://pypi.org/project/argon2-cffi/\">argon2-cffi</a> 包。可以通过运行 <code class=\"docutils literal notranslate\"><span class=\"pre\">python</span> <span class=\"pre\">-m</span> <span class=\"pre\">pip</span> <span class=\"pre\">install</span> <span class=\"pre\">django[argon2]</span></code> 来完成，这等同于 <code class=\"docutils literal notranslate\"><span class=\"pre\">python</span> <span class=\"pre\">-m</span> <span class=\"pre\">pip</span> <span class=\"pre\">install</span> <span class=\"pre\">argon2-cffi``（以及</span> <span class=\"pre\">Django</span> <span class=\"pre\">的</span> <span class=\"pre\">``pyproject.toml</span></code> 中的任何版本要求）。</p></li>\n<li><p>修改 <a class=\"reference internal\" href=\"/zh-hans/6.1/ref/settings/#std-setting-PASSWORD_HASHERS\"><code class=\"xref std std-setting docutils literal notranslate\"><span class=\"pre\">PASSWORD_HASHERS</span></code></a> 配置，把 <code class=\"docutils literal notranslate\"><span class=\"pre\">Argon2PasswordHasher</span></code> 放在首位。如下：</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"n\">PASSWORD_HASHERS</span> <span class=\"o\">=</span> <span class=\"p\">[</span>\n    <span class=\"s2\">&quot;django.contrib.auth.hashers.Argon2PasswordHasher&quot;</span><span class=\"p\">,</span>\n    <span class=\"s2\">&quot;django.contrib.auth.hashers.PBKDF2PasswordHasher&quot;</span><span class=\"p\">,</span>\n    <span class=\"s2\">&quot;django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher&quot;</span><span class=\"p\">,</span>\n    <span class=\"s2\">&quot;django.contrib.auth.hashers.BCryptSHA256PasswordHasher&quot;</span><span class=\"p\">,</span>\n    <span class=\"s2\">&quot;django.contrib.auth.hashers.ScryptPasswordHasher&quot;</span><span class=\"p\">,</span>\n<span class=\"p\">]</span>\n</code></pre></div>\n<p>如果你需要 Django 升级密码（ <a class=\"reference internal\" href=\"#password-upgrades\"><span class=\"std std-ref\">upgrade passwords</span></a> ），请保留或添加这个列表中的任何条目。</p>\n</li>\n</ol>\n</section>\n<section id=\"using-bcrypt-with-django\">\n<span id=\"bcrypt-usage\"></span><h3>在 Django 中使用 <code class=\"docutils literal notranslate\"><span class=\"pre\">bcrypt</span></code><a class=\"heading-anchor\" href=\"#using-bcrypt-with-django\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p><a class=\"reference external\" href=\"https://en.wikipedia.org/wiki/Bcrypt\">Bcrypt</a> 是一个非常流行的密码存储算法，尤其是为长期密码存储设计。Django 默认不使用它，因为它需要使用第三方库，但由于很多人想使用它，Django 只需要很少的努力就能支持 bcrypt。</p>\n<p>使用 Bcrypt 作为你的默认存储算法，需要以下步骤：</p>\n<ol class=\"arabic\">\n<li><p>安装 <a class=\"extlink-pypi reference external\" href=\"https://pypi.org/project/bcrypt/\">bcrypt</a> 包。可以通过运行 <code class=\"docutils literal notranslate\"><span class=\"pre\">python</span> <span class=\"pre\">-m</span> <span class=\"pre\">pip</span> <span class=\"pre\">install</span> <span class=\"pre\">django[bcrypt]</span></code> 来完成，这等同于 <code class=\"docutils literal notranslate\"><span class=\"pre\">python</span> <span class=\"pre\">-m</span> <span class=\"pre\">pip</span> <span class=\"pre\">install</span> <span class=\"pre\">bcrypt``（以及</span> <span class=\"pre\">Django</span> <span class=\"pre\">的</span> <span class=\"pre\">``pyproject.toml</span></code> 中的任何版本要求）。</p></li>\n<li><p>修改 <a class=\"reference internal\" href=\"/zh-hans/6.1/ref/settings/#std-setting-PASSWORD_HASHERS\"><code class=\"xref std std-setting docutils literal notranslate\"><span class=\"pre\">PASSWORD_HASHERS</span></code></a> 配置，把 <code class=\"docutils literal notranslate\"><span class=\"pre\">BCryptSHA256PasswordHasher</span></code> 放在首位。如下：</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"n\">PASSWORD_HASHERS</span> <span class=\"o\">=</span> <span class=\"p\">[</span>\n    <span class=\"s2\">&quot;django.contrib.auth.hashers.BCryptSHA256PasswordHasher&quot;</span><span class=\"p\">,</span>\n    <span class=\"s2\">&quot;django.contrib.auth.hashers.PBKDF2PasswordHasher&quot;</span><span class=\"p\">,</span>\n    <span class=\"s2\">&quot;django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher&quot;</span><span class=\"p\">,</span>\n    <span class=\"s2\">&quot;django.contrib.auth.hashers.Argon2PasswordHasher&quot;</span><span class=\"p\">,</span>\n    <span class=\"s2\">&quot;django.contrib.auth.hashers.ScryptPasswordHasher&quot;</span><span class=\"p\">,</span>\n<span class=\"p\">]</span>\n</code></pre></div>\n<p>如果你需要 Django 升级密码（ <a class=\"reference internal\" href=\"#password-upgrades\"><span class=\"std std-ref\">upgrade passwords</span></a> ），请保留或添加这个列表中的任何条目。</p>\n</li>\n</ol>\n<p>现在 Django 将使用 Bcrypt 作为默认存储算法。</p>\n</section>\n<section id=\"using-scrypt-with-django\">\n<span id=\"scrypt-usage\"></span><h3>在 Django 中使用 <code class=\"docutils literal notranslate\"><span class=\"pre\">scrypt</span></code><a class=\"heading-anchor\" href=\"#using-scrypt-with-django\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p><a class=\"reference external\" href=\"https://en.wikipedia.org/wiki/Scrypt\">scrypt</a> 类似于 PBKDF2 和 bcrypt，它们都利用一定数量的迭代来减慢暴力攻击的速度。然而，由于 PBKDF2 和 bcrypt 不需要大量内存，具有足够资源的攻击者可以发起大规模并行攻击以加快攻击过程。<a class=\"reference external\" href=\"https://en.wikipedia.org/wiki/Scrypt\">scrypt</a> 特别设计用于使用比其他基于密码的密钥派生函数更多的内存，以限制攻击者可以使用的并行度，详细信息请参见 <span class=\"target\" id=\"index-2\"></span><a class=\"rfc reference external\" href=\"https://datatracker.ietf.org/doc/html/rfc7914.html\"><strong>RFC 7914</strong></a>。</p>\n<p>使用 <a class=\"reference external\" href=\"https://en.wikipedia.org/wiki/Scrypt\">scrypt</a> 作为你的默认存储算法，需要以下步骤：</p>\n<ol class=\"arabic\">\n<li><p>将 <a class=\"reference internal\" href=\"/zh-hans/6.1/ref/settings/#std-setting-PASSWORD_HASHERS\"><code class=\"xref std std-setting docutils literal notranslate\"><span class=\"pre\">PASSWORD_HASHERS</span></code></a> 修改为首先列出 <code class=\"docutils literal notranslate\"><span class=\"pre\">ScryptPasswordHasher</span></code>。在您的设置文件中，应如下所示：</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"n\">PASSWORD_HASHERS</span> <span class=\"o\">=</span> <span class=\"p\">[</span>\n    <span class=\"s2\">&quot;django.contrib.auth.hashers.ScryptPasswordHasher&quot;</span><span class=\"p\">,</span>\n    <span class=\"s2\">&quot;django.contrib.auth.hashers.PBKDF2PasswordHasher&quot;</span><span class=\"p\">,</span>\n    <span class=\"s2\">&quot;django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher&quot;</span><span class=\"p\">,</span>\n    <span class=\"s2\">&quot;django.contrib.auth.hashers.Argon2PasswordHasher&quot;</span><span class=\"p\">,</span>\n    <span class=\"s2\">&quot;django.contrib.auth.hashers.BCryptSHA256PasswordHasher&quot;</span><span class=\"p\">,</span>\n<span class=\"p\">]</span>\n</code></pre></div>\n<p>如果你需要 Django 升级密码（ <a class=\"reference internal\" href=\"#password-upgrades\"><span class=\"std std-ref\">upgrade passwords</span></a> ），请保留或添加这个列表中的任何条目。</p>\n</li>\n</ol>\n<aside class=\"admonition admonition-note\" role=\"note\">\n<p class=\"admonition-title\">Note</p>\n<p><code class=\"docutils literal notranslate\"><span class=\"pre\">scrypt</span></code> 需要 OpenSSL 1.1及更高版本。</p>\n</aside>\n</section>\n<section id=\"increasing-the-salt-entropy\">\n<h3>增加盐的熵值<a class=\"heading-anchor\" href=\"#increasing-the-salt-entropy\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>大多数密码哈希值包括一个盐，与他们的密码哈希值一起，以防止彩虹表攻击。盐本身是一个随机值，它增加了彩虹表的大小和成本，目前在 <code class=\"docutils literal notranslate\"><span class=\"pre\">BasePasswordHasher</span></code> 中的 <code class=\"docutils literal notranslate\"><span class=\"pre\">salt_entropy</span></code> 值设置为 128 比特。随着计算和存储成本的降低，这个值应该被提高。当实现你自己的密码散列器时，你可以自由地覆盖这个值，以便为你的密码散列器使用一个理想的熵值。<code class=\"docutils literal notranslate\"><span class=\"pre\">salt_entropy</span></code> 是以比特为单位。</p>\n<aside class=\"admonition-implementation-detail admonition\">\n<p class=\"admonition-title\">实现细节</p>\n<p>由于盐值的存储方法，<code class=\"docutils literal notranslate\"><span class=\"pre\">salt_entropy</span></code> 值实际上是一个最小值。例如，一个 128 的值将提供一个实际包含 131 位熵的盐。</p>\n</aside>\n</section>\n<section id=\"increasing-the-work-factor\">\n<span id=\"increasing-password-algorithm-work-factor\"></span><h3>增加工作因子<a class=\"heading-anchor\" href=\"#increasing-the-work-factor\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<section id=\"pbkdf2-and-bcrypt\">\n<h4>PBKDF2 和 bcrypt<a class=\"heading-anchor\" href=\"#pbkdf2-and-bcrypt\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h4>\n<p>PBKDF2 和 bcrypt 算法使用一定数量的迭代或哈希轮次。这是有意为之的，它减慢了攻击者的速度，使对散列密码的攻击更加困难。然而，随着计算能力的增加，迭代次数也需要增加。我们选择了一个合理的默认值（并将在每个 Django 版本发布时逐渐增加），但您可以根据您的安全需求和可用的处理能力来调整它。要这样做，您需要子类化适当的算法并覆盖 <code class=\"docutils literal notranslate\"><span class=\"pre\">iterations</span></code> 参数（当子类化 bcrypt 哈希器时使用 <code class=\"docutils literal notranslate\"><span class=\"pre\">rounds</span></code> 参数）。例如，要增加默认 PBKDF2 算法使用的迭代次数：</p>\n<ol class=\"arabic\">\n<li><p>创建一个 <code class=\"docutils literal notranslate\"><span class=\"pre\">django.contrib.auth.hashers.PBKDF2PasswordHasher</span></code> 的子类：</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">django.contrib.auth.hashers</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">PBKDF2PasswordHasher</span>\n\n\n<span class=\"k\">class</span><span class=\"w\"> </span><span class=\"nc\">MyPBKDF2PasswordHasher</span><span class=\"p\">(</span><span class=\"n\">PBKDF2PasswordHasher</span><span class=\"p\">):</span>\n<span class=\"w\">    </span><span class=\"sd\">&quot;&quot;&quot;</span>\n<span class=\"sd\">    A subclass of PBKDF2PasswordHasher that uses 100 times more iterations.</span>\n<span class=\"sd\">    &quot;&quot;&quot;</span>\n\n    <span class=\"n\">iterations</span> <span class=\"o\">=</span> <span class=\"n\">PBKDF2PasswordHasher</span><span class=\"o\">.</span><span class=\"n\">iterations</span> <span class=\"o\">*</span> <span class=\"mi\">100</span>\n</code></pre></div>\n<p>在你的项目某些位置中保存。比如，你可以放在类似 <code class=\"docutils literal notranslate\"><span class=\"pre\">myproject/hashers.py</span></code> 里。</p>\n</li>\n<li><p>在 <a class=\"reference internal\" href=\"/zh-hans/6.1/ref/settings/#std-setting-PASSWORD_HASHERS\"><code class=\"xref std std-setting docutils literal notranslate\"><span class=\"pre\">PASSWORD_HASHERS</span></code></a> 中把新哈希放在首位：</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"n\">PASSWORD_HASHERS</span> <span class=\"o\">=</span> <span class=\"p\">[</span>\n    <span class=\"s2\">&quot;myproject.hashers.MyPBKDF2PasswordHasher&quot;</span><span class=\"p\">,</span>\n    <span class=\"s2\">&quot;django.contrib.auth.hashers.PBKDF2PasswordHasher&quot;</span><span class=\"p\">,</span>\n    <span class=\"s2\">&quot;django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher&quot;</span><span class=\"p\">,</span>\n    <span class=\"s2\">&quot;django.contrib.auth.hashers.Argon2PasswordHasher&quot;</span><span class=\"p\">,</span>\n    <span class=\"s2\">&quot;django.contrib.auth.hashers.BCryptSHA256PasswordHasher&quot;</span><span class=\"p\">,</span>\n    <span class=\"s2\">&quot;django.contrib.auth.hashers.ScryptPasswordHasher&quot;</span><span class=\"p\">,</span>\n<span class=\"p\">]</span>\n</code></pre></div>\n</li>\n</ol>\n<p>现在 Django 使用 PBKDF2 存储密码时将会多次迭代。</p>\n<aside class=\"admonition admonition-note\" role=\"note\">\n<p class=\"admonition-title\">Note</p>\n<p>bcrypt 的 <code class=\"docutils literal notranslate\"><span class=\"pre\">rounds</span></code> 是一个对数工作因子，例如，12 轮表示 <code class=\"docutils literal notranslate\"><span class=\"pre\">2</span> <span class=\"pre\">**</span> <span class=\"pre\">12</span></code> 次迭代。</p>\n</aside>\n</section>\n<section id=\"argon2\">\n<h4>Argon2<a class=\"heading-anchor\" href=\"#argon2\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h4>\n<p>Argon2 具有以下可自定义的属性：</p>\n<ol class=\"arabic simple\">\n<li><p><code class=\"docutils literal notranslate\"><span class=\"pre\">time_cost</span></code> 控制哈希的次数。</p></li>\n<li><p><code class=\"docutils literal notranslate\"><span class=\"pre\">memory_cost</span></code> 控制被用来计算哈希时的内存大小。</p></li>\n<li><p><code class=\"docutils literal notranslate\"><span class=\"pre\">parallelism</span></code> 控制并行计算哈希的 CPU 数量。</p></li>\n</ol>\n<p>这三个属性的默认值足够适合你。如果你确定密码哈希过快或过慢，可以按如下方式调整它：</p>\n<ol class=\"arabic simple\">\n<li><p>选择 <code class=\"docutils literal notranslate\"><span class=\"pre\">parallelism</span></code> 你可以节省计算哈希的线程数。</p></li>\n<li><p>选择 <code class=\"docutils literal notranslate\"><span class=\"pre\">memory_cost</span></code> 你可以节省内存的 KiB 。</p></li>\n<li><p>调整 <code class=\"docutils literal notranslate\"><span class=\"pre\">time_cost</span></code> 和估计哈希一个密码所需的时间。挑选出你可以接受的 <code class=\"docutils literal notranslate\"><span class=\"pre\">time_cost</span></code> 。如果设置为1的  <code class=\"docutils literal notranslate\"><span class=\"pre\">time_cost</span></code> 慢的无法接受，则调低 <code class=\"docutils literal notranslate\"><span class=\"pre\">memory_cost</span></code> 。</p></li>\n</ol>\n<aside class=\"admonition-memory-cost-interpretation admonition\">\n<p class=\"admonition-title\"><code class=\"docutils literal notranslate\"><span class=\"pre\">memory_cost</span></code> 说明</p>\n<p>argon2 命令行工具和一些其他的库解释了 <code class=\"docutils literal notranslate\"><span class=\"pre\">memory_cost</span></code> 参数不同于 Django 使用的值。换算公式是``memory_cost == 2 ** memory_cost_commandline`` 。</p>\n</aside>\n</section>\n<section id=\"scrypt\">\n<h4><code class=\"docutils literal notranslate\"><span class=\"pre\">scrypt</span></code><a class=\"heading-anchor\" href=\"#scrypt\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h4>\n<p><a class=\"reference external\" href=\"https://en.wikipedia.org/wiki/Scrypt\">scrypt</a> 具有以下可自定义的属性：</p>\n<ol class=\"arabic simple\">\n<li><p><code class=\"docutils literal notranslate\"><span class=\"pre\">work_factor</span></code> controls the number of iterations within the hash and the\nsize of memory for computation (<em>N</em>). It must be a power of 2.</p></li>\n<li><p><code class=\"docutils literal notranslate\"><span class=\"pre\">block_size</span></code> controls the internal block size (<em>r</em>), tuning the algorithm\nto memory latency.</p></li>\n<li><p><code class=\"docutils literal notranslate\"><span class=\"pre\">parallelism</span></code> controls how many independent computations may run in\nparallel (<em>p</em>).</p></li>\n<li><p><code class=\"docutils literal notranslate\"><span class=\"pre\">maxmem</span></code> 限制了在计算哈希过程中可以使用的最大内存大小。默认值为 <code class=\"docutils literal notranslate\"><span class=\"pre\">0</span></code>，表示使用 OpenSSL 库的默认限制。</p></li>\n</ol>\n<p>We've chosen reasonable defaults, but you may wish to tune it up or down,\ndepending on your security needs and available processing power and memory.</p>\n<aside class=\"admonition-estimating-memory-usage admonition\">\n<p class=\"admonition-title\">估算内存使用情况</p>\n<p><a class=\"reference external\" href=\"https://en.wikipedia.org/wiki/Scrypt\">scrypt</a> 的最低内存需求是：</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"n\">work_factor</span> <span class=\"o\">*</span> <span class=\"mi\">2</span> <span class=\"o\">*</span> <span class=\"n\">block_size</span> <span class=\"o\">*</span> <span class=\"mi\">64</span>\n</code></pre></div>\n<p>所以在更改 <code class=\"docutils literal notranslate\"><span class=\"pre\">work_factor</span></code> 或 <code class=\"docutils literal notranslate\"><span class=\"pre\">block_size</span></code> 值时，可能需要调整 <code class=\"docutils literal notranslate\"><span class=\"pre\">maxmem</span></code>。</p>\n<p>If the underlying implementation of <a class=\"reference external\" href=\"https://en.wikipedia.org/wiki/Scrypt\">scrypt</a> is fully multithreaded, the\nmemory requirement is multiplied by the <code class=\"docutils literal notranslate\"><span class=\"pre\">parallelism</span></code> value.</p>\n</aside>\n</section>\n</section>\n<section id=\"password-upgrading\">\n<span id=\"password-upgrades\"></span><h3>密码升级<a class=\"heading-anchor\" href=\"#password-upgrading\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>当用户登录时，如果用户的密码使用首选算法以外的算法保存，Django 会自动升级这个算法成为首选算法。这意味着旧的 Django 安装会在用户登录时自动得到更多的安全，并且当它们创建时你可以切换到新的更好的存储算法。</p>\n<p>然而，Django 只会使用 <a class=\"reference internal\" href=\"/zh-hans/6.1/ref/settings/#std-setting-PASSWORD_HASHERS\"><code class=\"xref std std-setting docutils literal notranslate\"><span class=\"pre\">PASSWORD_HASHERS</span></code></a> 提到的算法升级密码，因此当你升级到新系统时你要确保你从没有删除过这个列表的条目。如果你删除过，那么使用的没有列出的算法的用户将不会升级。当增加（或减少） PBKDF2 迭代的次数、bcrypt 的轮次或者 argon2 属性，哈希过的密码将被更新。</p>\n<p>注意，如果数据库内的所有密码没有在默认哈希算法里编码，则由于非默认算法的密码编码的用户登录请求持续时间和不存在用户（运行过默认哈希）的登录请求持续时间的不同，你可能会受到用户枚举时间攻击。你可以使用 <a class=\"reference internal\" href=\"#wrapping-password-hashers\"><span class=\"std std-ref\">升级旧密码的哈希值</span></a> 来缓解此问题。</p>\n</section>\n<section id=\"password-upgrading-without-requiring-a-login\">\n<span id=\"wrapping-password-hashers\"></span><h3>无需登录的密码升级<a class=\"heading-anchor\" href=\"#password-upgrading-without-requiring-a-login\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>如果你有一个使用较旧、较弱的哈希算法（如 MD5）的现有数据库，你可能希望自己升级这些哈希值，而不是等待用户登录时进行升级（如果用户不返回你的网站，可能永远不会发生）。在这种情况下，你可以使用一个“包装”的密码哈希器。</p>\n<p>在这个示例中，我们将迁移一组 MD5 哈希值，以使用 PBKDF2(MD5(password))，并添加相应的密码哈希器，用于在登录时检查用户输入的正确密码。我们假设我们正在使用内置的“User”模型，并且我们的项目有一个“accounts”应用程序。您可以根据需要修改模式，以适应任何算法或自定义用户模型。</p>\n<p>首先，我们添加一个自定义的哈希：</p>\n<figure class=\"code-block code-block-captioned\" data-language=\"python\"><figcaption class=\"code-block-caption\"><code class=\"docutils literal notranslate\"><span class=\"pre\">accounts/hashers.py</span></code></figcaption>\n<div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Python code\"><code><span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">django.contrib.auth.hashers</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"p\">(</span>\n    <span class=\"n\">PBKDF2PasswordHasher</span><span class=\"p\">,</span>\n    <span class=\"n\">MD5PasswordHasher</span><span class=\"p\">,</span>\n<span class=\"p\">)</span>\n\n\n<span class=\"k\">class</span><span class=\"w\"> </span><span class=\"nc\">PBKDF2WrappedMD5PasswordHasher</span><span class=\"p\">(</span><span class=\"n\">PBKDF2PasswordHasher</span><span class=\"p\">):</span>\n    <span class=\"n\">algorithm</span> <span class=\"o\">=</span> <span class=\"s2\">&quot;pbkdf2_wrapped_md5&quot;</span>\n\n    <span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">encode_md5_hash</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">,</span> <span class=\"n\">md5_hash</span><span class=\"p\">,</span> <span class=\"n\">salt</span><span class=\"p\">,</span> <span class=\"n\">iterations</span><span class=\"o\">=</span><span class=\"kc\">None</span><span class=\"p\">):</span>\n        <span class=\"k\">return</span> <span class=\"nb\">super</span><span class=\"p\">()</span><span class=\"o\">.</span><span class=\"n\">encode</span><span class=\"p\">(</span><span class=\"n\">md5_hash</span><span class=\"p\">,</span> <span class=\"n\">salt</span><span class=\"p\">,</span> <span class=\"n\">iterations</span><span class=\"p\">)</span>\n\n    <span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">encode</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">,</span> <span class=\"n\">password</span><span class=\"p\">,</span> <span class=\"n\">salt</span><span class=\"p\">,</span> <span class=\"n\">iterations</span><span class=\"o\">=</span><span class=\"kc\">None</span><span class=\"p\">):</span>\n        <span class=\"n\">_</span><span class=\"p\">,</span> <span class=\"n\">_</span><span class=\"p\">,</span> <span class=\"n\">md5_hash</span> <span class=\"o\">=</span> <span class=\"n\">MD5PasswordHasher</span><span class=\"p\">()</span><span class=\"o\">.</span><span class=\"n\">encode</span><span class=\"p\">(</span><span class=\"n\">password</span><span class=\"p\">,</span> <span class=\"n\">salt</span><span class=\"p\">)</span><span class=\"o\">.</span><span class=\"n\">split</span><span class=\"p\">(</span><span class=\"s2\">&quot;$&quot;</span><span class=\"p\">,</span> <span class=\"mi\">2</span><span class=\"p\">)</span>\n        <span class=\"k\">return</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">encode_md5_hash</span><span class=\"p\">(</span><span class=\"n\">md5_hash</span><span class=\"p\">,</span> <span class=\"n\">salt</span><span class=\"p\">,</span> <span class=\"n\">iterations</span><span class=\"p\">)</span>\n</code></pre></figure>\n<p>数据迁移可能类似于这样：</p>\n<figure class=\"code-block code-block-captioned\" data-language=\"python\"><figcaption class=\"code-block-caption\"><code class=\"docutils literal notranslate\"><span class=\"pre\">accounts/migrations/0002_migrate_md5_passwords.py</span></code></figcaption>\n<div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Python code\"><code><span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">django.db</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">migrations</span>\n\n<span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">..hashers</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">PBKDF2WrappedMD5PasswordHasher</span>\n\n\n<span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">forwards_func</span><span class=\"p\">(</span><span class=\"n\">apps</span><span class=\"p\">,</span> <span class=\"n\">schema_editor</span><span class=\"p\">):</span>\n    <span class=\"n\">User</span> <span class=\"o\">=</span> <span class=\"n\">apps</span><span class=\"o\">.</span><span class=\"n\">get_model</span><span class=\"p\">(</span><span class=\"s2\">&quot;auth&quot;</span><span class=\"p\">,</span> <span class=\"s2\">&quot;User&quot;</span><span class=\"p\">)</span>\n    <span class=\"n\">users</span> <span class=\"o\">=</span> <span class=\"n\">User</span><span class=\"o\">.</span><span class=\"n\">objects</span><span class=\"o\">.</span><span class=\"n\">filter</span><span class=\"p\">(</span><span class=\"n\">password__startswith</span><span class=\"o\">=</span><span class=\"s2\">&quot;md5$&quot;</span><span class=\"p\">)</span>\n    <span class=\"n\">hasher</span> <span class=\"o\">=</span> <span class=\"n\">PBKDF2WrappedMD5PasswordHasher</span><span class=\"p\">()</span>\n    <span class=\"k\">for</span> <span class=\"n\">user</span> <span class=\"ow\">in</span> <span class=\"n\">users</span><span class=\"p\">:</span>\n        <span class=\"n\">algorithm</span><span class=\"p\">,</span> <span class=\"n\">salt</span><span class=\"p\">,</span> <span class=\"n\">md5_hash</span> <span class=\"o\">=</span> <span class=\"n\">user</span><span class=\"o\">.</span><span class=\"n\">password</span><span class=\"o\">.</span><span class=\"n\">split</span><span class=\"p\">(</span><span class=\"s2\">&quot;$&quot;</span><span class=\"p\">,</span> <span class=\"mi\">2</span><span class=\"p\">)</span>\n        <span class=\"n\">user</span><span class=\"o\">.</span><span class=\"n\">password</span> <span class=\"o\">=</span> <span class=\"n\">hasher</span><span class=\"o\">.</span><span class=\"n\">encode_md5_hash</span><span class=\"p\">(</span><span class=\"n\">md5_hash</span><span class=\"p\">,</span> <span class=\"n\">salt</span><span class=\"p\">)</span>\n        <span class=\"n\">user</span><span class=\"o\">.</span><span class=\"n\">save</span><span class=\"p\">(</span><span class=\"n\">update_fields</span><span class=\"o\">=</span><span class=\"p\">[</span><span class=\"s2\">&quot;password&quot;</span><span class=\"p\">])</span>\n\n\n<span class=\"k\">class</span><span class=\"w\"> </span><span class=\"nc\">Migration</span><span class=\"p\">(</span><span class=\"n\">migrations</span><span class=\"o\">.</span><span class=\"n\">Migration</span><span class=\"p\">):</span>\n    <span class=\"n\">dependencies</span> <span class=\"o\">=</span> <span class=\"p\">[</span>\n        <span class=\"p\">(</span><span class=\"s2\">&quot;accounts&quot;</span><span class=\"p\">,</span> <span class=\"s2\">&quot;0001_initial&quot;</span><span class=\"p\">),</span>\n        <span class=\"c1\"># replace this with the latest migration in contrib.auth</span>\n        <span class=\"p\">(</span><span class=\"s2\">&quot;auth&quot;</span><span class=\"p\">,</span> <span class=\"s2\">&quot;####_migration_name&quot;</span><span class=\"p\">),</span>\n    <span class=\"p\">]</span>\n\n    <span class=\"n\">operations</span> <span class=\"o\">=</span> <span class=\"p\">[</span>\n        <span class=\"n\">migrations</span><span class=\"o\">.</span><span class=\"n\">RunPython</span><span class=\"p\">(</span><span class=\"n\">forwards_func</span><span class=\"p\">),</span>\n    <span class=\"p\">]</span>\n</code></pre></figure>\n<p>注意，迁移将对上千名用户花费大约数十分钟，这取决于你的硬件速度。</p>\n<p>最后，我们在 <a class=\"reference internal\" href=\"/zh-hans/6.1/ref/settings/#std-setting-PASSWORD_HASHERS\"><code class=\"xref std std-setting docutils literal notranslate\"><span class=\"pre\">PASSWORD_HASHERS</span></code></a> 中添加配置：</p>\n<figure class=\"code-block code-block-captioned\" data-language=\"python\"><figcaption class=\"code-block-caption\"><code class=\"docutils literal notranslate\"><span class=\"pre\">mysite/settings.py</span></code></figcaption>\n<div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Python code\"><code><span class=\"n\">PASSWORD_HASHERS</span> <span class=\"o\">=</span> <span class=\"p\">[</span>\n    <span class=\"s2\">&quot;django.contrib.auth.hashers.PBKDF2PasswordHasher&quot;</span><span class=\"p\">,</span>\n    <span class=\"s2\">&quot;accounts.hashers.PBKDF2WrappedMD5PasswordHasher&quot;</span><span class=\"p\">,</span>\n<span class=\"p\">]</span>\n</code></pre></figure>\n<p>包含你的站点使用的此列表中的其他算法。</p>\n</section>\n<section id=\"included-hashers\">\n<span id=\"auth-included-hashers\"></span><h3>已包含的哈希<a class=\"heading-anchor\" href=\"#included-hashers\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>在 Django 中的所有列出的哈希是：</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"p\">[</span>\n    <span class=\"s2\">&quot;django.contrib.auth.hashers.PBKDF2PasswordHasher&quot;</span><span class=\"p\">,</span>\n    <span class=\"s2\">&quot;django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher&quot;</span><span class=\"p\">,</span>\n    <span class=\"s2\">&quot;django.contrib.auth.hashers.Argon2PasswordHasher&quot;</span><span class=\"p\">,</span>\n    <span class=\"s2\">&quot;django.contrib.auth.hashers.BCryptSHA256PasswordHasher&quot;</span><span class=\"p\">,</span>\n    <span class=\"s2\">&quot;django.contrib.auth.hashers.BCryptPasswordHasher&quot;</span><span class=\"p\">,</span>\n    <span class=\"s2\">&quot;django.contrib.auth.hashers.ScryptPasswordHasher&quot;</span><span class=\"p\">,</span>\n    <span class=\"s2\">&quot;django.contrib.auth.hashers.MD5PasswordHasher&quot;</span><span class=\"p\">,</span>\n<span class=\"p\">]</span>\n</code></pre></div>\n<p>相应的算法名是：</p>\n<ul class=\"simple\">\n<li><p><code class=\"docutils literal notranslate\"><span class=\"pre\">pbkdf2_sha256</span></code></p></li>\n<li><p><code class=\"docutils literal notranslate\"><span class=\"pre\">pbkdf2_sha1</span></code></p></li>\n<li><p><code class=\"docutils literal notranslate\"><span class=\"pre\">argon2</span></code></p></li>\n<li><p><code class=\"docutils literal notranslate\"><span class=\"pre\">bcrypt_sha256</span></code></p></li>\n<li><p><code class=\"docutils literal notranslate\"><span class=\"pre\">bcrypt</span></code></p></li>\n<li><p><code class=\"docutils literal notranslate\"><span class=\"pre\">scrypt</span></code></p></li>\n<li><p><code class=\"docutils literal notranslate\"><span class=\"pre\">md5</span></code></p></li>\n</ul>\n</section>\n<section id=\"writing-your-own-hasher\">\n<span id=\"write-your-own-password-hasher\"></span><h3>编写你自己的哈希<a class=\"heading-anchor\" href=\"#writing-your-own-hasher\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>如果你编写自己的密码哈希包含工作因子，比如迭代数量。你应该实现一个 <code class=\"docutils literal notranslate\"><span class=\"pre\">harden_runtime(self,</span> <span class=\"pre\">password,</span> <span class=\"pre\">encoded)</span></code> 方法来消除编码密码时提供的工作因子和默认的哈希工作因子之间的运行时间差。这样可以防止用户枚举时间攻击，因为旧的迭代次数中对密码编码的用户与不存在的用户（运行默认哈希的默认迭代次数）在登录时存在差异。</p>\n<p>以 PDKDF2 为例，如果编码包含20000次迭代，并且默认哈希迭代是30000，那么该方法应该通过另外的10000次迭代的 PBKDF2 运行密码。</p>\n<p>如果你的哈希没有工作因子，可以将该方法实现为 no-op (pass) 。</p>\n</section>\n</section>\n<section id=\"module-django.contrib.auth.hashers\">\n<span id=\"manually-managing-a-user-s-password\"></span><h2>手动管理用户的密码<a class=\"heading-anchor\" href=\"#module-django.contrib.auth.hashers\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>The <a class=\"reference internal\" href=\"#module-django.contrib.auth.hashers\" title=\"django.contrib.auth.hashers\"><code class=\"xref py py-mod docutils literal notranslate\"><span class=\"pre\">django.contrib.auth.hashers</span></code></a> module provides a set of functions\nto create and validate hashed passwords. You can use them independently\nfrom the <code class=\"docutils literal notranslate\"><span class=\"pre\">User</span></code> model.</p>\n<dl class=\"py function\">\n<dt class=\"sig sig-object py\" id=\"django.contrib.auth.hashers.check_password\">\n<span class=\"sig-name descname\"><span class=\"pre\">check_password</span></span><span class=\"sig-paren\">(</span><em class=\"sig-param\"><span class=\"n\"><span class=\"pre\">password</span></span></em>, <em class=\"sig-param\"><span class=\"n\"><span class=\"pre\">encoded</span></span></em>, <em class=\"sig-param\"><span class=\"n\"><span class=\"pre\">setter</span></span><span class=\"o\"><span class=\"pre\">=</span></span><span class=\"default_value\"><span class=\"pre\">None</span></span></em>, <em class=\"sig-param\"><span class=\"n\"><span class=\"pre\">preferred</span></span><span class=\"o\"><span class=\"pre\">=</span></span><span class=\"default_value\"><span class=\"pre\">'default'</span></span></em><span class=\"sig-paren\">)</span><a class=\"heading-anchor\" href=\"#django.contrib.auth.hashers.check_password\"><span class=\"visually-hidden\">Link to this definition</span><span aria-hidden=\"true\">#</span></a></dt>\n<dd></dd></dl>\n\n<dl class=\"py function\">\n<dt class=\"sig sig-object py\" id=\"django.contrib.auth.hashers.acheck_password\">\n<span class=\"sig-name descname\"><span class=\"pre\">acheck_password</span></span><span class=\"sig-paren\">(</span><em class=\"sig-param\"><span class=\"n\"><span class=\"pre\">password</span></span></em>, <em class=\"sig-param\"><span class=\"n\"><span class=\"pre\">encoded</span></span></em>, <em class=\"sig-param\"><span class=\"n\"><span class=\"pre\">asetter</span></span><span class=\"o\"><span class=\"pre\">=</span></span><span class=\"default_value\"><span class=\"pre\">None</span></span></em>, <em class=\"sig-param\"><span class=\"n\"><span class=\"pre\">preferred</span></span><span class=\"o\"><span class=\"pre\">=</span></span><span class=\"default_value\"><span class=\"pre\">'default'</span></span></em><span class=\"sig-paren\">)</span><a class=\"heading-anchor\" href=\"#django.contrib.auth.hashers.acheck_password\"><span class=\"visually-hidden\">Link to this definition</span><span aria-hidden=\"true\">#</span></a></dt>\n<dd><p><em>异步版本</em>：<code class=\"docutils literal notranslate\"><span class=\"pre\">acheck_password()</span></code></p>\n<p>If you'd like to manually authenticate a user by comparing a plaintext\npassword to the hashed password in the database, use the convenience\nfunction <a class=\"reference internal\" href=\"#django.contrib.auth.hashers.check_password\" title=\"django.contrib.auth.hashers.check_password\"><code class=\"xref py py-func docutils literal notranslate\"><span class=\"pre\">check_password()</span></code></a>. It takes two mandatory arguments: the\nplaintext password to check, and the full value of a user's <code class=\"docutils literal notranslate\"><span class=\"pre\">password</span></code>\nfield in the database to check against. It returns <code class=\"docutils literal notranslate\"><span class=\"pre\">True</span></code> if they match,\n<code class=\"docutils literal notranslate\"><span class=\"pre\">False</span></code> otherwise. Optionally, you can pass a callable <code class=\"docutils literal notranslate\"><span class=\"pre\">setter</span></code> that\ntakes the password and will be called when you need to regenerate it. You\ncan also pass <code class=\"docutils literal notranslate\"><span class=\"pre\">preferred</span></code> to change a hashing algorithm if you don't want\nto use the default (first entry of <code class=\"docutils literal notranslate\"><span class=\"pre\">PASSWORD_HASHERS</span></code> setting). See\n<a class=\"reference internal\" href=\"#auth-included-hashers\"><span class=\"std std-ref\">已包含的哈希</span></a> for the algorithm name of each hasher.</p>\n</dd></dl>\n\n<dl class=\"py function\">\n<dt class=\"sig sig-object py\" id=\"django.contrib.auth.hashers.make_password\">\n<span class=\"sig-name descname\"><span class=\"pre\">make_password</span></span><span class=\"sig-paren\">(</span><em class=\"sig-param\"><span class=\"n\"><span class=\"pre\">password</span></span></em>, <em class=\"sig-param\"><span class=\"n\"><span class=\"pre\">salt</span></span><span class=\"o\"><span class=\"pre\">=</span></span><span class=\"default_value\"><span class=\"pre\">None</span></span></em>, <em class=\"sig-param\"><span class=\"n\"><span class=\"pre\">hasher</span></span><span class=\"o\"><span class=\"pre\">=</span></span><span class=\"default_value\"><span class=\"pre\">'default'</span></span></em><span class=\"sig-paren\">)</span><a class=\"heading-anchor\" href=\"#django.contrib.auth.hashers.make_password\"><span class=\"visually-hidden\">Link to this definition</span><span aria-hidden=\"true\">#</span></a></dt>\n<dd><p>Creates a hashed password in the format used by this application. It takes\none mandatory argument: the plaintext password (string or bytes).\nOptionally, you can provide a salt and a hashing algorithm to use, if you\ndon't want to use the defaults (first entry of <code class=\"docutils literal notranslate\"><span class=\"pre\">PASSWORD_HASHERS</span></code>\nsetting). See <a class=\"reference internal\" href=\"#auth-included-hashers\"><span class=\"std std-ref\">已包含的哈希</span></a> for the algorithm name of each\nhasher. If the password argument is <code class=\"docutils literal notranslate\"><span class=\"pre\">None</span></code>, an unusable password is\nreturned (one that will never be accepted by <a class=\"reference internal\" href=\"#django.contrib.auth.hashers.check_password\" title=\"django.contrib.auth.hashers.check_password\"><code class=\"xref py py-func docutils literal notranslate\"><span class=\"pre\">check_password()</span></code></a>).</p>\n</dd></dl>\n\n<dl class=\"py function\">\n<dt class=\"sig sig-object py\" id=\"django.contrib.auth.hashers.is_password_usable\">\n<span class=\"sig-name descname\"><span class=\"pre\">is_password_usable</span></span><span class=\"sig-paren\">(</span><em class=\"sig-param\"><span class=\"n\"><span class=\"pre\">encoded_password</span></span></em><span class=\"sig-paren\">)</span><a class=\"heading-anchor\" href=\"#django.contrib.auth.hashers.is_password_usable\"><span class=\"visually-hidden\">Link to this definition</span><span aria-hidden=\"true\">#</span></a></dt>\n<dd><p>如果密码是 <a class=\"reference internal\" href=\"/zh-hans/6.1/ref/contrib/auth/#django.contrib.auth.models.User.set_unusable_password\" title=\"django.contrib.auth.models.User.set_unusable_password\"><code class=\"xref py py-meth docutils literal notranslate\"><span class=\"pre\">User.set_unusable_password()</span></code></a> 的结果，则返回 <code class=\"docutils literal notranslate\"><span class=\"pre\">False</span></code> 。</p>\n</dd></dl>\n\n</section>\n<section id=\"module-django.contrib.auth.password_validation\">\n<span id=\"id3\"></span><span id=\"password-validation\"></span><h2>密码验证<a class=\"heading-anchor\" href=\"#module-django.contrib.auth.password_validation\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>用户经常会选择弱密码。为了缓解这个问题，Django 提供可插拔的密码验证。你可以同时配置多个密码验证。Django 已经包含了一些验证，但你也可以编写你自己的验证。</p>\n<p>每个密码验证器必须提供帮助文本以解释要求给用户，验证给定的密码，如果不符合要求则返回错误消息，并可选择定义一个回调函数，以在用户密码更改时进行通知。验证器还可以具有可选的设置，以微调其行为。</p>\n<p>验证由 <a class=\"reference internal\" href=\"/zh-hans/6.1/ref/settings/#std-setting-AUTH_PASSWORD_VALIDATORS\"><code class=\"xref std std-setting docutils literal notranslate\"><span class=\"pre\">AUTH_PASSWORD_VALIDATORS</span></code></a> 控制。默认的设置是一个空列表，这意味着默认是不验证的。在使用默认的 <a class=\"reference internal\" href=\"/zh-hans/6.1/ref/django-admin/#django-admin-startproject\"><code class=\"xref std std-djadmin docutils literal notranslate\"><span class=\"pre\">startproject</span></code></a> 创建的新项目中，默认启用了验证器集合。</p>\n<p>默认情况下，验证器在重置或修改密码的表单中使用，也可以在 <a class=\"reference internal\" href=\"/zh-hans/6.1/ref/django-admin/#django-admin-createsuperuser\"><code class=\"xref std std-djadmin docutils literal notranslate\"><span class=\"pre\">createsuperuser</span></code></a> 和 <a class=\"reference internal\" href=\"/zh-hans/6.1/ref/django-admin/#django-admin-changepassword\"><code class=\"xref std std-djadmin docutils literal notranslate\"><span class=\"pre\">changepassword</span></code></a> 命令中使用。验证器不能应用在模型层，比如 <code class=\"docutils literal notranslate\"><span class=\"pre\">User.objects.create_user()</span></code> 和 <code class=\"docutils literal notranslate\"><span class=\"pre\">create_superuser()</span></code> ，因为我们假设开发者（非用户）会在模型层与 Django 进行交互，也因为模型验证不会在创建模型时自动运行。</p>\n<aside class=\"admonition admonition-note\" role=\"note\">\n<p class=\"admonition-title\">Note</p>\n<p>密码验证器可以防止使用很多类型的弱密码。但是，密码通过所有的验证器并不能保证它就是强密码。这里有很多因素削弱即便最先进的密码验证程序也检测不到的密码。</p>\n</aside>\n<section id=\"enabling-password-validation\">\n<h3>启用密码验证<a class=\"heading-anchor\" href=\"#enabling-password-validation\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>在 <a class=\"reference internal\" href=\"/zh-hans/6.1/ref/settings/#std-setting-AUTH_PASSWORD_VALIDATORS\"><code class=\"xref std std-setting docutils literal notranslate\"><span class=\"pre\">AUTH_PASSWORD_VALIDATORS</span></code></a> 中设置密码验证：</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"n\">AUTH_PASSWORD_VALIDATORS</span> <span class=\"o\">=</span> <span class=\"p\">[</span>\n    <span class=\"p\">{</span>\n        <span class=\"s2\">&quot;NAME&quot;</span><span class=\"p\">:</span> <span class=\"s2\">&quot;django.contrib.auth.password_validation.UserAttributeSimilarityValidator&quot;</span><span class=\"p\">,</span>\n    <span class=\"p\">},</span>\n    <span class=\"p\">{</span>\n        <span class=\"s2\">&quot;NAME&quot;</span><span class=\"p\">:</span> <span class=\"s2\">&quot;django.contrib.auth.password_validation.MinimumLengthValidator&quot;</span><span class=\"p\">,</span>\n        <span class=\"s2\">&quot;OPTIONS&quot;</span><span class=\"p\">:</span> <span class=\"p\">{</span>\n            <span class=\"s2\">&quot;min_length&quot;</span><span class=\"p\">:</span> <span class=\"mi\">9</span><span class=\"p\">,</span>\n        <span class=\"p\">},</span>\n    <span class=\"p\">},</span>\n    <span class=\"p\">{</span>\n        <span class=\"s2\">&quot;NAME&quot;</span><span class=\"p\">:</span> <span class=\"s2\">&quot;django.contrib.auth.password_validation.CommonPasswordValidator&quot;</span><span class=\"p\">,</span>\n    <span class=\"p\">},</span>\n    <span class=\"p\">{</span>\n        <span class=\"s2\">&quot;NAME&quot;</span><span class=\"p\">:</span> <span class=\"s2\">&quot;django.contrib.auth.password_validation.NumericPasswordValidator&quot;</span><span class=\"p\">,</span>\n    <span class=\"p\">},</span>\n<span class=\"p\">]</span>\n</code></pre></div>\n<p>这个例子启用了所有包含的验证器：</p>\n<ul class=\"simple\">\n<li><p><code class=\"docutils literal notranslate\"><span class=\"pre\">UserAttributeSimilarityValidator</span></code> 检查密码和一组用户属性集合之间的相似性。</p></li>\n<li><p><code class=\"docutils literal notranslate\"><span class=\"pre\">MinimumLengthValidator</span></code> 用来检查密码是否符合最小长度。这个验证器可以自定义设置：它现在需要最短9位字符，而不是默认的8个字符。</p></li>\n<li><p><code class=\"docutils literal notranslate\"><span class=\"pre\">CommonPasswordValidator</span></code> 检查密码是否在常用密码列表中。默认情况下，它会与列表中的2000个常用密码作比较。</p></li>\n<li><p><code class=\"docutils literal notranslate\"><span class=\"pre\">NumericPasswordValidator</span></code> 检查密码是否是完全是数字的。</p></li>\n</ul>\n<p>对于 <code class=\"docutils literal notranslate\"><span class=\"pre\">UserAttributeSimilarityValidator</span></code> 和 <code class=\"docutils literal notranslate\"><span class=\"pre\">CommonPasswordValidator</span></code> ，我们在这个例子里使用默认配置。<code class=\"docutils literal notranslate\"><span class=\"pre\">NumericPasswordValidator</span></code> 不需要设置。</p>\n<p>帮助文本和来自密码验证器的任何错误信息始终按照  <a class=\"reference internal\" href=\"/zh-hans/6.1/ref/settings/#std-setting-AUTH_PASSWORD_VALIDATORS\"><code class=\"xref std std-setting docutils literal notranslate\"><span class=\"pre\">AUTH_PASSWORD_VALIDATORS</span></code></a> 列出的顺序返回。</p>\n</section>\n<section id=\"included-validators\">\n<span id=\"included-password-validators\"></span><h3>已包含的验证器<a class=\"heading-anchor\" href=\"#included-validators\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>Django 包含了四种验证器：</p>\n<dl class=\"py class\">\n<dt class=\"sig sig-object py\" id=\"django.contrib.auth.password_validation.MinimumLengthValidator\">\n<em class=\"property\"><span class=\"k\"><span class=\"pre\">class</span></span><span class=\"w\"> </span></em><span class=\"sig-name descname\"><span class=\"pre\">MinimumLengthValidator</span></span><span class=\"sig-paren\">(</span><em class=\"sig-param\"><span class=\"n\"><span class=\"pre\">min_length</span></span><span class=\"o\"><span class=\"pre\">=</span></span><span class=\"default_value\"><span class=\"pre\">8</span></span></em><span class=\"sig-paren\">)</span><a class=\"heading-anchor\" href=\"#django.contrib.auth.password_validation.MinimumLengthValidator\"><span class=\"visually-hidden\">Link to this definition</span><span aria-hidden=\"true\">#</span></a></dt>\n<dd><p>验证密码是否达到最小长度要求。最小长度可以使用 <code class=\"docutils literal notranslate\"><span class=\"pre\">min_length</span></code> 参数进行自定义。</p>\n<dl class=\"py method\">\n<dt class=\"sig sig-object py\" id=\"django.contrib.auth.password_validation.MinimumLengthValidator.get_error_message\">\n<span class=\"sig-name descname\"><span class=\"pre\">get_error_message</span></span><span class=\"sig-paren\">(</span><span class=\"sig-paren\">)</span><a class=\"heading-anchor\" href=\"#django.contrib.auth.password_validation.MinimumLengthValidator.get_error_message\"><span class=\"visually-hidden\">Link to this definition</span><span aria-hidden=\"true\">#</span></a></dt>\n<dd><p>A hook for customizing the <code class=\"docutils literal notranslate\"><span class=\"pre\">ValidationError</span></code> error message. Defaults\nto <code class=\"docutils literal notranslate\"><span class=\"pre\">&quot;This</span> <span class=\"pre\">password</span> <span class=\"pre\">is</span> <span class=\"pre\">too</span> <span class=\"pre\">short.</span> <span class=\"pre\">It</span> <span class=\"pre\">must</span> <span class=\"pre\">contain</span> <span class=\"pre\">at</span> <span class=\"pre\">least</span> <span class=\"pre\">&lt;min_length&gt;</span>\n<span class=\"pre\">characters.&quot;</span></code>.</p>\n</dd></dl>\n\n<dl class=\"py method\">\n<dt class=\"sig sig-object py\" id=\"django.contrib.auth.password_validation.MinimumLengthValidator.get_help_text\">\n<span class=\"sig-name descname\"><span class=\"pre\">get_help_text</span></span><span class=\"sig-paren\">(</span><span class=\"sig-paren\">)</span><a class=\"heading-anchor\" href=\"#django.contrib.auth.password_validation.MinimumLengthValidator.get_help_text\"><span class=\"visually-hidden\">Link to this definition</span><span aria-hidden=\"true\">#</span></a></dt>\n<dd><p>A hook for customizing the validator's help text. Defaults to <code class=\"docutils literal notranslate\"><span class=\"pre\">&quot;Your</span>\n<span class=\"pre\">password</span> <span class=\"pre\">must</span> <span class=\"pre\">contain</span> <span class=\"pre\">at</span> <span class=\"pre\">least</span> <span class=\"pre\">&lt;min_length&gt;</span> <span class=\"pre\">characters.&quot;</span></code>.</p>\n</dd></dl>\n\n</dd></dl>\n\n<dl class=\"py class\">\n<dt class=\"sig sig-object py\" id=\"django.contrib.auth.password_validation.UserAttributeSimilarityValidator\">\n<em class=\"property\"><span class=\"k\"><span class=\"pre\">class</span></span><span class=\"w\"> </span></em><span class=\"sig-name descname\"><span class=\"pre\">UserAttributeSimilarityValidator</span></span><span class=\"sig-paren\">(</span><em class=\"sig-param\"><span class=\"n\"><span class=\"pre\">user_attributes</span></span><span class=\"o\"><span class=\"pre\">=</span></span><span class=\"default_value\"><span class=\"pre\">DEFAULT_USER_ATTRIBUTES</span></span></em>, <em class=\"sig-param\"><span class=\"n\"><span class=\"pre\">max_similarity</span></span><span class=\"o\"><span class=\"pre\">=</span></span><span class=\"default_value\"><span class=\"pre\">0.7</span></span></em><span class=\"sig-paren\">)</span><a class=\"heading-anchor\" href=\"#django.contrib.auth.password_validation.UserAttributeSimilarityValidator\"><span class=\"visually-hidden\">Link to this definition</span><span aria-hidden=\"true\">#</span></a></dt>\n<dd><p>验证密码与用户的某些属性足够不同。</p>\n<p><code class=\"docutils literal notranslate\"><span class=\"pre\">user_attributes</span></code> 参数应该是可比较的用户属性名的可迭代参数。如果没有提供这个参数，默认使用：<code class=\"docutils literal notranslate\"><span class=\"pre\">'username',</span> <span class=\"pre\">'first_name',</span> <span class=\"pre\">'last_name',</span> <span class=\"pre\">'email'</span></code> 。不存在的属性会被忽略。</p>\n<p>最大允许的密码相似度可以通过 <code class=\"docutils literal notranslate\"><span class=\"pre\">max_similarity</span></code> 参数设置在 0.1 到 1.0 的范围内。这将与 <a class=\"reference external\" href=\"https://docs.python.org/3/library/difflib.html#difflib.SequenceMatcher.quick_ratio\" title=\"(in Python v3.14)\"><code class=\"xref py py-meth docutils literal notranslate\"><span class=\"pre\">difflib.SequenceMatcher.quick_ratio()</span></code></a> 的结果进行比较。0.1 的值会拒绝与 <code class=\"docutils literal notranslate\"><span class=\"pre\">user_attributes</span></code> 非常不同的密码，而 1.0 的值只会拒绝与属性值相同的密码。</p>\n<dl class=\"py method\">\n<dt class=\"sig sig-object py\" id=\"django.contrib.auth.password_validation.UserAttributeSimilarityValidator.get_error_message\">\n<span class=\"sig-name descname\"><span class=\"pre\">get_error_message</span></span><span class=\"sig-paren\">(</span><span class=\"sig-paren\">)</span><a class=\"heading-anchor\" href=\"#django.contrib.auth.password_validation.UserAttributeSimilarityValidator.get_error_message\"><span class=\"visually-hidden\">Link to this definition</span><span aria-hidden=\"true\">#</span></a></dt>\n<dd><p>A hook for customizing the <code class=\"docutils literal notranslate\"><span class=\"pre\">ValidationError</span></code> error message. Defaults\nto <code class=\"docutils literal notranslate\"><span class=\"pre\">&quot;The</span> <span class=\"pre\">password</span> <span class=\"pre\">is</span> <span class=\"pre\">too</span> <span class=\"pre\">similar</span> <span class=\"pre\">to</span> <span class=\"pre\">the</span> <span class=\"pre\">&lt;user_attribute&gt;.&quot;</span></code>.</p>\n</dd></dl>\n\n<dl class=\"py method\">\n<dt class=\"sig sig-object py\" id=\"django.contrib.auth.password_validation.UserAttributeSimilarityValidator.get_help_text\">\n<span class=\"sig-name descname\"><span class=\"pre\">get_help_text</span></span><span class=\"sig-paren\">(</span><span class=\"sig-paren\">)</span><a class=\"heading-anchor\" href=\"#django.contrib.auth.password_validation.UserAttributeSimilarityValidator.get_help_text\"><span class=\"visually-hidden\">Link to this definition</span><span aria-hidden=\"true\">#</span></a></dt>\n<dd><p>A hook for customizing the validator's help text. Defaults to <code class=\"docutils literal notranslate\"><span class=\"pre\">&quot;Your</span>\n<span class=\"pre\">password</span> <span class=\"pre\">can’t</span> <span class=\"pre\">be</span> <span class=\"pre\">too</span> <span class=\"pre\">similar</span> <span class=\"pre\">to</span> <span class=\"pre\">your</span> <span class=\"pre\">other</span> <span class=\"pre\">personal</span> <span class=\"pre\">information.&quot;</span></code>.</p>\n</dd></dl>\n\n</dd></dl>\n\n<dl class=\"py class\">\n<dt class=\"sig sig-object py\" id=\"django.contrib.auth.password_validation.CommonPasswordValidator\">\n<em class=\"property\"><span class=\"k\"><span class=\"pre\">class</span></span><span class=\"w\"> </span></em><span class=\"sig-name descname\"><span class=\"pre\">CommonPasswordValidator</span></span><span class=\"sig-paren\">(</span><em class=\"sig-param\"><span class=\"n\"><span class=\"pre\">password_list_path</span></span><span class=\"o\"><span class=\"pre\">=</span></span><span class=\"default_value\"><span class=\"pre\">DEFAULT_PASSWORD_LIST_PATH</span></span></em><span class=\"sig-paren\">)</span><a class=\"heading-anchor\" href=\"#django.contrib.auth.password_validation.CommonPasswordValidator\"><span class=\"visually-hidden\">Link to this definition</span><span aria-hidden=\"true\">#</span></a></dt>\n<dd><p>Validates that the password is not a common password. This converts the\npassword to lowercase (to do a case-insensitive comparison) and checks it\nagainst a list of 20,000 common passwords created by <a class=\"reference external\" href=\"https://gist.github.com/roycewilliams/226886fd01572964e1431ac8afc999ce\">Royce Williams</a>.</p>\n<p><code class=\"docutils literal notranslate\"><span class=\"pre\">password_list_path</span></code> 用来设置自定义的常用密码列表文件的路径。这个文件应该每行包含一个小写密码，并且文件是纯文本或 gzip 压缩过的。</p>\n<dl class=\"py method\">\n<dt class=\"sig sig-object py\" id=\"django.contrib.auth.password_validation.CommonPasswordValidator.get_error_message\">\n<span class=\"sig-name descname\"><span class=\"pre\">get_error_message</span></span><span class=\"sig-paren\">(</span><span class=\"sig-paren\">)</span><a class=\"heading-anchor\" href=\"#django.contrib.auth.password_validation.CommonPasswordValidator.get_error_message\"><span class=\"visually-hidden\">Link to this definition</span><span aria-hidden=\"true\">#</span></a></dt>\n<dd><p>A hook for customizing the <code class=\"docutils literal notranslate\"><span class=\"pre\">ValidationError</span></code> error message. Defaults\nto <code class=\"docutils literal notranslate\"><span class=\"pre\">&quot;This</span> <span class=\"pre\">password</span> <span class=\"pre\">is</span> <span class=\"pre\">too</span> <span class=\"pre\">common.&quot;</span></code>.</p>\n</dd></dl>\n\n<dl class=\"py method\">\n<dt class=\"sig sig-object py\" id=\"django.contrib.auth.password_validation.CommonPasswordValidator.get_help_text\">\n<span class=\"sig-name descname\"><span class=\"pre\">get_help_text</span></span><span class=\"sig-paren\">(</span><span class=\"sig-paren\">)</span><a class=\"heading-anchor\" href=\"#django.contrib.auth.password_validation.CommonPasswordValidator.get_help_text\"><span class=\"visually-hidden\">Link to this definition</span><span aria-hidden=\"true\">#</span></a></dt>\n<dd><p>A hook for customizing the validator's help text. Defaults to <code class=\"docutils literal notranslate\"><span class=\"pre\">&quot;Your</span>\n<span class=\"pre\">password</span> <span class=\"pre\">can’t</span> <span class=\"pre\">be</span> <span class=\"pre\">a</span> <span class=\"pre\">commonly</span> <span class=\"pre\">used</span> <span class=\"pre\">password.&quot;</span></code>.</p>\n</dd></dl>\n\n</dd></dl>\n\n<dl class=\"py class\">\n<dt class=\"sig sig-object py\" id=\"django.contrib.auth.password_validation.NumericPasswordValidator\">\n<em class=\"property\"><span class=\"k\"><span class=\"pre\">class</span></span><span class=\"w\"> </span></em><span class=\"sig-name descname\"><span class=\"pre\">NumericPasswordValidator</span></span><a class=\"heading-anchor\" href=\"#django.contrib.auth.password_validation.NumericPasswordValidator\"><span class=\"visually-hidden\">Link to this definition</span><span aria-hidden=\"true\">#</span></a></dt>\n<dd><p>验证密码不是完全由数字组成。</p>\n<dl class=\"py method\">\n<dt class=\"sig sig-object py\" id=\"django.contrib.auth.password_validation.NumericPasswordValidator.get_error_message\">\n<span class=\"sig-name descname\"><span class=\"pre\">get_error_message</span></span><span class=\"sig-paren\">(</span><span class=\"sig-paren\">)</span><a class=\"heading-anchor\" href=\"#django.contrib.auth.password_validation.NumericPasswordValidator.get_error_message\"><span class=\"visually-hidden\">Link to this definition</span><span aria-hidden=\"true\">#</span></a></dt>\n<dd><p>A hook for customizing the <code class=\"docutils literal notranslate\"><span class=\"pre\">ValidationError</span></code> error message. Defaults\nto <code class=\"docutils literal notranslate\"><span class=\"pre\">&quot;This</span> <span class=\"pre\">password</span> <span class=\"pre\">is</span> <span class=\"pre\">entirely</span> <span class=\"pre\">numeric.&quot;</span></code>.</p>\n</dd></dl>\n\n<dl class=\"py method\">\n<dt class=\"sig sig-object py\" id=\"django.contrib.auth.password_validation.NumericPasswordValidator.get_help_text\">\n<span class=\"sig-name descname\"><span class=\"pre\">get_help_text</span></span><span class=\"sig-paren\">(</span><span class=\"sig-paren\">)</span><a class=\"heading-anchor\" href=\"#django.contrib.auth.password_validation.NumericPasswordValidator.get_help_text\"><span class=\"visually-hidden\">Link to this definition</span><span aria-hidden=\"true\">#</span></a></dt>\n<dd><p>A hook for customizing the validator's help text. Defaults to <code class=\"docutils literal notranslate\"><span class=\"pre\">&quot;Your</span>\n<span class=\"pre\">password</span> <span class=\"pre\">can’t</span> <span class=\"pre\">be</span> <span class=\"pre\">entirely</span> <span class=\"pre\">numeric.&quot;</span></code>.</p>\n</dd></dl>\n\n</dd></dl>\n\n</section>\n<section id=\"integrating-validation\">\n<h3>集成检查<a class=\"heading-anchor\" href=\"#integrating-validation\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p><code class=\"docutils literal notranslate\"><span class=\"pre\">django.contrib.auth.password_validation</span></code> 包含一些你可以在表单或其他地方调用的函数，用来集成密码检查。如果你使用自定义表单来进行密码设置或者你有允许密码设置的 API 调用，此功能会很有用。</p>\n<dl class=\"py function\">\n<dt class=\"sig sig-object py\" id=\"django.contrib.auth.password_validation.validate_password\">\n<span class=\"sig-name descname\"><span class=\"pre\">validate_password</span></span><span class=\"sig-paren\">(</span><em class=\"sig-param\"><span class=\"n\"><span class=\"pre\">password</span></span></em>, <em class=\"sig-param\"><span class=\"n\"><span class=\"pre\">user</span></span><span class=\"o\"><span class=\"pre\">=</span></span><span class=\"default_value\"><span class=\"pre\">None</span></span></em>, <em class=\"sig-param\"><span class=\"n\"><span class=\"pre\">password_validators</span></span><span class=\"o\"><span class=\"pre\">=</span></span><span class=\"default_value\"><span class=\"pre\">None</span></span></em><span class=\"sig-paren\">)</span><a class=\"heading-anchor\" href=\"#django.contrib.auth.password_validation.validate_password\"><span class=\"visually-hidden\">Link to this definition</span><span aria-hidden=\"true\">#</span></a></dt>\n<dd><p>验证密码。如果所有验证器验证密码有效，则返回 <code class=\"docutils literal notranslate\"><span class=\"pre\">None</span></code> 。如果一个或多个验证器拒绝此密码，将会引发 <a class=\"reference internal\" href=\"/zh-hans/6.1/ref/exceptions/#django.core.exceptions.ValidationError\" title=\"django.core.exceptions.ValidationError\"><code class=\"xref py py-exc docutils literal notranslate\"><span class=\"pre\">ValidationError</span></code></a> 和验证器的错误信息。</p>\n<p><code class=\"docutils literal notranslate\"><span class=\"pre\">user</span></code> 对象是可选的：如果不提供用户对象，一些验证器将不能执行验证，并将接受所有密码。</p>\n</dd></dl>\n\n<dl class=\"py function\">\n<dt class=\"sig sig-object py\" id=\"django.contrib.auth.password_validation.password_changed\">\n<span class=\"sig-name descname\"><span class=\"pre\">password_changed</span></span><span class=\"sig-paren\">(</span><em class=\"sig-param\"><span class=\"n\"><span class=\"pre\">password</span></span></em>, <em class=\"sig-param\"><span class=\"n\"><span class=\"pre\">user</span></span><span class=\"o\"><span class=\"pre\">=</span></span><span class=\"default_value\"><span class=\"pre\">None</span></span></em>, <em class=\"sig-param\"><span class=\"n\"><span class=\"pre\">password_validators</span></span><span class=\"o\"><span class=\"pre\">=</span></span><span class=\"default_value\"><span class=\"pre\">None</span></span></em><span class=\"sig-paren\">)</span><a class=\"heading-anchor\" href=\"#django.contrib.auth.password_validation.password_changed\"><span class=\"visually-hidden\">Link to this definition</span><span aria-hidden=\"true\">#</span></a></dt>\n<dd><p>通知所有验证器密码已经更改。这可以由验证器使用，例如防止密码重用。一旦密码更改成功，则调用此方法。</p>\n<p>对于 <a class=\"reference internal\" href=\"/zh-hans/6.1/topics/auth/customizing/#django.contrib.auth.models.AbstractBaseUser\" title=\"django.contrib.auth.models.AbstractBaseUser\"><code class=\"xref py py-class docutils literal notranslate\"><span class=\"pre\">AbstractBaseUser</span></code></a> 子类，当调用 <a class=\"reference internal\" href=\"/zh-hans/6.1/topics/auth/customizing/#django.contrib.auth.models.AbstractBaseUser.set_password\" title=\"django.contrib.auth.models.AbstractBaseUser.set_password\"><code class=\"xref py py-meth docutils literal notranslate\"><span class=\"pre\">set_password()</span></code></a> 是会将密码字段标记为 &quot;dirty&quot; ，这会在用户保存后调用 <code class=\"docutils literal notranslate\"><span class=\"pre\">password_changed()</span></code> 。</p>\n</dd></dl>\n\n<dl class=\"py function\">\n<dt class=\"sig sig-object py\" id=\"django.contrib.auth.password_validation.password_validators_help_texts\">\n<span class=\"sig-name descname\"><span class=\"pre\">password_validators_help_texts</span></span><span class=\"sig-paren\">(</span><em class=\"sig-param\"><span class=\"n\"><span class=\"pre\">password_validators</span></span><span class=\"o\"><span class=\"pre\">=</span></span><span class=\"default_value\"><span class=\"pre\">None</span></span></em><span class=\"sig-paren\">)</span><a class=\"heading-anchor\" href=\"#django.contrib.auth.password_validation.password_validators_help_texts\"><span class=\"visually-hidden\">Link to this definition</span><span aria-hidden=\"true\">#</span></a></dt>\n<dd><p>返回一个所有验证器帮助文案的列表。这些向用户解释了密码要求。</p>\n</dd></dl>\n\n<dl class=\"py function\">\n<dt class=\"sig sig-object py\" id=\"django.contrib.auth.password_validation.password_validators_help_text_html\">\n<span class=\"sig-name descname\"><span class=\"pre\">password_validators_help_text_html</span></span><span class=\"sig-paren\">(</span><em class=\"sig-param\"><span class=\"n\"><span class=\"pre\">password_validators</span></span><span class=\"o\"><span class=\"pre\">=</span></span><span class=\"default_value\"><span class=\"pre\">None</span></span></em><span class=\"sig-paren\">)</span><a class=\"heading-anchor\" href=\"#django.contrib.auth.password_validation.password_validators_help_text_html\"><span class=\"visually-hidden\">Link to this definition</span><span aria-hidden=\"true\">#</span></a></dt>\n<dd><p>返回一个``&lt;ul&gt;`` ，包含所有帮助文案的 HTML 字符串。这在表单中添加密码验证时有帮助，因为你可以直接将输出传递到表单字段的 <code class=\"docutils literal notranslate\"><span class=\"pre\">help_text</span></code> 参数。</p>\n</dd></dl>\n\n<dl class=\"py function\">\n<dt class=\"sig sig-object py\" id=\"django.contrib.auth.password_validation.get_password_validators\">\n<span class=\"sig-name descname\"><span class=\"pre\">get_password_validators</span></span><span class=\"sig-paren\">(</span><em class=\"sig-param\"><span class=\"n\"><span class=\"pre\">validator_config</span></span></em><span class=\"sig-paren\">)</span><a class=\"heading-anchor\" href=\"#django.contrib.auth.password_validation.get_password_validators\"><span class=\"visually-hidden\">Link to this definition</span><span aria-hidden=\"true\">#</span></a></dt>\n<dd><p>返回一个基于 <code class=\"docutils literal notranslate\"><span class=\"pre\">validator_config</span></code> 的验证器对象的集合。默认情况下，所有函数使用 <a class=\"reference internal\" href=\"/zh-hans/6.1/ref/settings/#std-setting-AUTH_PASSWORD_VALIDATORS\"><code class=\"xref std std-setting docutils literal notranslate\"><span class=\"pre\">AUTH_PASSWORD_VALIDATORS</span></code></a> 定义的验证器，但通过一个验证器替代集合来调用此函数，然后向其他函数传递的密码验证器参数传递结果，将使用你自定义的验证器集合。当你有一个应用于大多数场景的通用的验证器集合时，需要一个自定义的集合来用于特殊情况。当你始终使用同一个验证器集合时，则不需要这个函数，因为默认使用是 <a class=\"reference internal\" href=\"/zh-hans/6.1/ref/settings/#std-setting-AUTH_PASSWORD_VALIDATORS\"><code class=\"xref std std-setting docutils literal notranslate\"><span class=\"pre\">AUTH_PASSWORD_VALIDATORS</span></code></a> 的配置。</p>\n<p><code class=\"docutils literal notranslate\"><span class=\"pre\">validator_config</span></code> 的结构和 <a class=\"reference internal\" href=\"/zh-hans/6.1/ref/settings/#std-setting-AUTH_PASSWORD_VALIDATORS\"><code class=\"xref std std-setting docutils literal notranslate\"><span class=\"pre\">AUTH_PASSWORD_VALIDATORS</span></code></a> 的结构相同。这个函数的返回值可以传递给上述函数列表的``password_validators`` 参数。</p>\n</dd></dl>\n\n<p>注意，如果将密码传递给其中一个函数，应该始终是明文密码，而不是哈希过的密码。</p>\n</section>\n<section id=\"writing-your-own-validator\">\n<h3>编写自定义的验证器<a class=\"heading-anchor\" href=\"#writing-your-own-validator\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>如果 Django 内置的验证器不满足你的需求，你可以编写自定义的验证器。验证器的接口很小。它们必须实现两个方法：</p>\n<ul class=\"simple\">\n<li><p><code class=\"docutils literal notranslate\"><span class=\"pre\">validate(self,</span> <span class=\"pre\">password,</span> <span class=\"pre\">user=None)</span></code> ：验证密码。如果密码有效，返回 <code class=\"docutils literal notranslate\"><span class=\"pre\">None</span></code> ，否则引发 <a class=\"reference internal\" href=\"/zh-hans/6.1/ref/exceptions/#django.core.exceptions.ValidationError\" title=\"django.core.exceptions.ValidationError\"><code class=\"xref py py-exc docutils literal notranslate\"><span class=\"pre\">ValidationError</span></code></a> 错误。你必须能够处理 <code class=\"docutils literal notranslate\"><span class=\"pre\">user</span></code> 为 <code class=\"docutils literal notranslate\"><span class=\"pre\">None</span></code> 的情况，如果这样会让验证器无法运行，只需返回 <code class=\"docutils literal notranslate\"><span class=\"pre\">None</span></code> 即可。</p></li>\n<li><p><code class=\"docutils literal notranslate\"><span class=\"pre\">get_help_text()</span></code> ：提供一个帮助文本向用户解释密码要求。</p></li>\n</ul>\n<p>验证器的 <a class=\"reference internal\" href=\"/zh-hans/6.1/ref/settings/#std-setting-AUTH_PASSWORD_VALIDATORS\"><code class=\"xref std std-setting docutils literal notranslate\"><span class=\"pre\">AUTH_PASSWORD_VALIDATORS</span></code></a> 中， <code class=\"docutils literal notranslate\"><span class=\"pre\">OPTIONS</span></code> 里的任何条目将会传递到构造器中。所有构造器参数应该有一个默认值。</p>\n<p>这里是一个验证器的基本示例，其中包含一个可选的设置：</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">django.core.exceptions</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">ValidationError</span>\n<span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">django.utils.translation</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">gettext</span> <span class=\"k\">as</span> <span class=\"n\">_</span>\n\n\n<span class=\"k\">class</span><span class=\"w\"> </span><span class=\"nc\">MinimumLengthValidator</span><span class=\"p\">:</span>\n    <span class=\"k\">def</span><span class=\"w\"> </span><span class=\"fm\">__init__</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">,</span> <span class=\"n\">min_length</span><span class=\"o\">=</span><span class=\"mi\">8</span><span class=\"p\">):</span>\n        <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">min_length</span> <span class=\"o\">=</span> <span class=\"n\">min_length</span>\n\n    <span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">validate</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">,</span> <span class=\"n\">password</span><span class=\"p\">,</span> <span class=\"n\">user</span><span class=\"o\">=</span><span class=\"kc\">None</span><span class=\"p\">):</span>\n        <span class=\"k\">if</span> <span class=\"nb\">len</span><span class=\"p\">(</span><span class=\"n\">password</span><span class=\"p\">)</span> <span class=\"o\">&lt;</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">min_length</span><span class=\"p\">:</span>\n            <span class=\"k\">raise</span> <span class=\"n\">ValidationError</span><span class=\"p\">(</span>\n                <span class=\"n\">_</span><span class=\"p\">(</span><span class=\"s2\">&quot;This password must contain at least </span><span class=\"si\">%(min_length)d</span><span class=\"s2\"> characters.&quot;</span><span class=\"p\">),</span>\n                <span class=\"n\">code</span><span class=\"o\">=</span><span class=\"s2\">&quot;password_too_short&quot;</span><span class=\"p\">,</span>\n                <span class=\"n\">params</span><span class=\"o\">=</span><span class=\"p\">{</span><span class=\"s2\">&quot;min_length&quot;</span><span class=\"p\">:</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">min_length</span><span class=\"p\">},</span>\n            <span class=\"p\">)</span>\n\n    <span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">get_help_text</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n        <span class=\"k\">return</span> <span class=\"n\">_</span><span class=\"p\">(</span>\n            <span class=\"s2\">&quot;Your password must contain at least </span><span class=\"si\">%(min_length)d</span><span class=\"s2\"> characters.&quot;</span>\n            <span class=\"o\">%</span> <span class=\"p\">{</span><span class=\"s2\">&quot;min_length&quot;</span><span class=\"p\">:</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">min_length</span><span class=\"p\">}</span>\n        <span class=\"p\">)</span>\n</code></pre></div>\n<p>你也可以实现 <code class=\"docutils literal notranslate\"><span class=\"pre\">password_changed(password,</span> <span class=\"pre\">user=None)</span></code> ，在密码修改成功后调用。比如说用来防止密码重用。但是，如果你决定存储用户之前的密码，则不应该以明文形式存储。</p>\n</section>\n</section>","rootId":"password-management-in-django","toc":[{"title":"Django 如何存储密码","anchor":"how-django-stores-passwords","children":[{"title":"在 Django 中使用 Argon2","anchor":"using-argon2-with-django","children":[]},{"title":"在 Django 中使用 bcrypt","anchor":"using-bcrypt-with-django","children":[]},{"title":"在 Django 中使用 scrypt","anchor":"using-scrypt-with-django","children":[]},{"title":"增加盐的熵值","anchor":"increasing-the-salt-entropy","children":[]},{"title":"增加工作因子","anchor":"increasing-the-work-factor","children":[{"title":"PBKDF2 和 bcrypt","anchor":"pbkdf2-and-bcrypt","children":[]},{"title":"Argon2","anchor":"argon2","children":[]},{"title":"scrypt","anchor":"scrypt","children":[]}]},{"title":"密码升级","anchor":"password-upgrading","children":[]},{"title":"无需登录的密码升级","anchor":"password-upgrading-without-requiring-a-login","children":[]},{"title":"已包含的哈希","anchor":"included-hashers","children":[]},{"title":"编写你自己的哈希","anchor":"writing-your-own-hasher","children":[]}]},{"title":"手动管理用户的密码","anchor":"module-django.contrib.auth.hashers","children":[]},{"title":"密码验证","anchor":"module-django.contrib.auth.password_validation","children":[{"title":"启用密码验证","anchor":"enabling-password-validation","children":[]},{"title":"已包含的验证器","anchor":"included-validators","children":[]},{"title":"集成检查","anchor":"integrating-validation","children":[]},{"title":"编写自定义的验证器","anchor":"writing-your-own-validator","children":[]}]}],"breadcrumbs":[{"docname":"topics/index","title":"使用 Django","url":"/zh-hans/6.1/topics/"},{"docname":"topics/auth/index","title":"Django中的用户认证","url":"/zh-hans/6.1/topics/auth/"}],"prev":{"docname":"topics/auth/default","title":"使用 Django 的验证系统","url":"/zh-hans/6.1/topics/auth/default/"},"next":{"docname":"topics/auth/customizing","title":"Django 中的自定义验证","url":"/zh-hans/6.1/topics/auth/customizing/"},"formats":{"html":"/zh-hans/6.1/topics/auth/passwords/","markdown":"/zh-hans/6.1/topics/auth/passwords.md","json":"/zh-hans/6.1/topics/auth/passwords.json"},"source":"https://github.com/django/django/blob/stable/6.1.x/docs/topics/auth/passwords.txt","official":"https://docs.djangoproject.com/zh-hans/6.1/topics/auth/passwords/","inVersions":["6.1","6.0","5.2","5.1","5.0","4.2","4.1","4.0","3.2","3.1","3.0","2.2","2.1","2.0"],"inLocales":["en","sv","zh-hans","ga","fr","ja","id","it","pt-br","ko","es","el","pl"]}