From 0e2ca1cacb0a6e7bf88476a85bd3c9bb1278f009 Mon Sep 17 00:00:00 2001 From: jaystone776 Date: Tue, 26 Sep 2023 07:06:09 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=8C=90=20Update=20Chinese=20translation?= =?UTF-8?q?=20for=20`docs/tutorial/security/simple-oauth2.md`=20(#3844)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Sebastián Ramírez Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .../docs/tutorial/security/simple-oauth2.md | 214 +++++++++--------- 1 file changed, 111 insertions(+), 103 deletions(-) diff --git a/docs/zh/docs/tutorial/security/simple-oauth2.md b/docs/zh/docs/tutorial/security/simple-oauth2.md index 276f3d63b..c7f46177f 100644 --- a/docs/zh/docs/tutorial/security/simple-oauth2.md +++ b/docs/zh/docs/tutorial/security/simple-oauth2.md @@ -1,94 +1,98 @@ -# 使用密码和 Bearer 的简单 OAuth2 +# OAuth2 实现简单的 Password 和 Bearer 验证 -现在让我们接着上一章继续开发,并添加缺少的部分以实现一个完整的安全性流程。 +本章添加上一章示例中欠缺的部分,实现完整的安全流。 ## 获取 `username` 和 `password` -我们将使用 **FastAPI** 的安全性实用工具来获取 `username` 和 `password`。 +首先,使用 **FastAPI** 安全工具获取 `username` 和 `password`。 -OAuth2 规定在使用(我们打算用的)「password 流程」时,客户端/用户必须将 `username` 和 `password` 字段作为表单数据发送。 +OAuth2 规范要求使用**密码流**时,客户端或用户必须以表单数据形式发送 `username` 和 `password` 字段。 -而且规范明确了字段必须这样命名。因此 `user-name` 或 `email` 是行不通的。 +并且,这两个字段必须命名为 `username` 和 `password` ,不能使用 `user-name` 或 `email` 等其它名称。 -不过不用担心,你可以在前端按照你的想法将它展示给最终用户。 +不过也不用担心,前端仍可以显示终端用户所需的名称。 -而且你的数据库模型也可以使用你想用的任何其他名称。 +数据库模型也可以使用所需的名称。 -但是对于登录*路径操作*,我们需要使用这些名称来与规范兼容(以具备例如使用集成的 API 文档系统的能力)。 +但对于登录*路径操作*,则要使用兼容规范的 `username` 和 `password`,(例如,实现与 API 文档集成)。 -规范还写明了 `username` 和 `password` 必须作为表单数据发送(因此,此处不能使用 JSON)。 +该规范要求必须以表单数据形式发送 `username` 和 `password`,因此,不能使用 JSON 对象。 -### `scope` +### `Scope`(作用域) -规范还提到客户端可以发送另一个表单字段「`scope`」。 +OAuth2 还支持客户端发送**`scope`**表单字段。 -这个表单字段的名称为 `scope`(单数形式),但实际上它是一个由空格分隔的「作用域」组成的长字符串。 +虽然表单字段的名称是 `scope`(单数),但实际上,它是以空格分隔的,由多个**scope**组成的长字符串。 -每个「作用域」只是一个字符串(中间没有空格)。 +**作用域**只是不带空格的字符串。 -它们通常用于声明特定的安全权限,例如: +常用于声明指定安全权限,例如: -* `users:read` 或者 `users:write` 是常见的例子。 -* Facebook / Instagram 使用 `instagram_basic`。 -* Google 使用了 `https://www.googleapis.com/auth/drive` 。 +* 常见用例为,`users:read` 或 `users:write` +* 脸书和 Instagram 使用 `instagram_basic` +* 谷歌使用 `https://www.googleapis.com/auth/drive` -!!! info - 在 OAuth2 中「作用域」只是一个声明所需特定权限的字符串。 +!!! info "说明" - 它有没有 `:` 这样的其他字符或者是不是 URL 都没有关系。 + OAuth2 中,**作用域**只是声明指定权限的字符串。 - 这些细节是具体的实现。 + 是否使用冒号 `:` 等符号,或是不是 URL 并不重要。 - 对 OAuth2 来说它们就只是字符串而已。 + 这些细节只是特定的实现方式。 + + 对 OAuth2 来说,都只是字符串而已。 ## 获取 `username` 和 `password` 的代码 -现在,让我们使用 **FastAPI** 提供的实用工具来处理此问题。 +接下来,使用 **FastAPI** 工具获取用户名与密码。 ### `OAuth2PasswordRequestForm` -首先,导入 `OAuth2PasswordRequestForm`,然后在 `token` 的*路径操作*中通过 `Depends` 将其作为依赖项使用。 +首先,导入 `OAuth2PasswordRequestForm`,然后,在 `/token` *路径操作* 中,用 `Depends` 把该类作为依赖项。 ```Python hl_lines="4 76" {!../../../docs_src/security/tutorial003.py!} ``` -`OAuth2PasswordRequestForm` 是一个类依赖项,声明了如下的请求表单: +`OAuth2PasswordRequestForm` 是用以下几项内容声明表单请求体的类依赖项: + +* `username` +* `password` +* 可选的 `scope` 字段,由多个空格分隔的字符串组成的长字符串 +* 可选的 `grant_type` -* `username`。 -* `password`。 -* 一个可选的 `scope` 字段,是一个由空格分隔的字符串组成的大字符串。 -* 一个可选的 `grant_type`. +!!! tip "提示" -!!! tip - OAuth2 规范实际上*要求* `grant_type` 字段使用一个固定的值 `password`,但是 `OAuth2PasswordRequestForm` 没有作强制约束。 + 实际上,OAuth2 规范*要求* `grant_type` 字段使用固定值 `password`,但 `OAuth2PasswordRequestForm` 没有作强制约束。 - 如果你需要强制要求这一点,请使用 `OAuth2PasswordRequestFormStrict` 而不是 `OAuth2PasswordRequestForm`。 + 如需强制使用固定值 `password`,则不要用 `OAuth2PasswordRequestForm`,而是用 `OAuth2PasswordRequestFormStrict`。 -* 一个可选的 `client_id`(我们的示例不需要它)。 -* 一个可选的 `client_secret`(我们的示例不需要它)。 +* 可选的 `client_id`(本例未使用) +* 可选的 `client_secret`(本例未使用) -!!! info - `OAuth2PasswordRequestForm` 并不像 `OAuth2PasswordBearer` 一样是 FastAPI 的一个特殊的类。 +!!! info "说明" - `OAuth2PasswordBearer` 使得 **FastAPI** 明白它是一个安全方案。所以它得以通过这种方式添加到 OpenAPI 中。 + `OAuth2PasswordRequestForm` 与 `OAuth2PasswordBearer` 一样,都不是 FastAPI 的特殊类。 - 但 `OAuth2PasswordRequestForm` 只是一个你可以自己编写的类依赖项,或者你也可以直接声明 `Form` 参数。 + **FastAPI** 把 `OAuth2PasswordBearer` 识别为安全方案。因此,可以通过这种方式把它添加至 OpenAPI。 - 但是由于这是一种常见的使用场景,因此 FastAPI 出于简便直接提供了它。 + 但 `OAuth2PasswordRequestForm` 只是可以自行编写的类依赖项,也可以直接声明 `Form` 参数。 + + 但由于这种用例很常见,FastAPI 为了简便,就直接提供了对它的支持。 ### 使用表单数据 -!!! tip - 类依赖项 `OAuth2PasswordRequestForm` 的实例不会有用空格分隔的长字符串属性 `scope`,而是具有一个 `scopes` 属性,该属性将包含实际被发送的每个作用域字符串组成的列表。 +!!! tip "提示" + + `OAuth2PasswordRequestForm` 类依赖项的实例没有以空格分隔的长字符串属性 `scope`,但它支持 `scopes` 属性,由已发送的 scope 字符串列表组成。 - 在此示例中我们没有使用 `scopes`,但如果你需要的话可以使用该功能。 + 本例没有使用 `scopes`,但开发者也可以根据需要使用该属性。 -现在,使用表单字段中的 `username` 从(伪)数据库中获取用户数据。 +现在,即可使用表单字段 `username`,从(伪)数据库中获取用户数据。 -如果没有这个用户,我们将返回一个错误消息,提示「用户名或密码错误」。 +如果不存在指定用户,则返回错误消息,提示**用户名或密码错误**。 -对于这个错误,我们使用 `HTTPException` 异常: +本例使用 `HTTPException` 异常显示此错误: ```Python hl_lines="3 77-79" {!../../../docs_src/security/tutorial003.py!} @@ -96,27 +100,27 @@ OAuth2 规定在使用(我们打算用的)「password 流程」时,客户 ### 校验密码 -目前我们已经从数据库中获取了用户数据,但尚未校验密码。 +至此,我们已经从数据库中获取了用户数据,但尚未校验密码。 -让我们首先将这些数据放入 Pydantic `UserInDB` 模型中。 +接下来,首先将数据放入 Pydantic 的 `UserInDB` 模型。 -永远不要保存明文密码,因此,我们将使用(伪)哈希密码系统。 +注意:永远不要保存明文密码,本例暂时先使用(伪)哈希密码系统。 -如果密码不匹配,我们将返回同一个错误。 +如果密码不匹配,则返回与上面相同的错误。 -#### 哈希密码 +#### 密码哈希 -「哈希」的意思是:将某些内容(在本例中为密码)转换为看起来像乱码的字节序列(只是一个字符串)。 +**哈希**是指,将指定内容(本例中为密码)转换为形似乱码的字节序列(其实就是字符串)。 -每次你传入完全相同的内容(完全相同的密码)时,你都会得到完全相同的乱码。 +每次传入完全相同的内容(比如,完全相同的密码)时,得到的都是完全相同的乱码。 -但是你不能从乱码转换回密码。 +但这个乱码无法转换回传入的密码。 -##### 为什么使用哈希密码 +##### 为什么使用密码哈希 -如果你的数据库被盗,小偷将无法获得用户的明文密码,只有哈希值。 +原因很简单,假如数据库被盗,窃贼无法获取用户的明文密码,得到的只是哈希值。 -因此,小偷将无法尝试在另一个系统中使用这些相同的密码(由于许多用户在任何地方都使用相同的密码,因此这很危险)。 +这样一来,窃贼就无法在其它应用中使用窃取的密码,要知道,很多用户在所有系统中都使用相同的密码,风险超大。 ```Python hl_lines="80-83" {!../../../docs_src/security/tutorial003.py!} @@ -124,9 +128,9 @@ OAuth2 规定在使用(我们打算用的)「password 流程」时,客户 #### 关于 `**user_dict` -`UserInDB(**user_dict)` 表示: +`UserInDB(**user_dict)` 是指: -*直接将 `user_dict` 的键和值作为关键字参数传递,等同于:* +*直接把 `user_dict` 的键与值当作关键字参数传递,等效于:* ```Python UserInDB( @@ -138,75 +142,79 @@ UserInDB( ) ``` -!!! info - 有关 `user_dict` 的更完整说明,请参阅[**额外的模型**文档](../extra-models.md#about-user_indict){.internal-link target=_blank}。 +!!! info "说明" -## 返回令牌 + `user_dict` 的说明,详见[**更多模型**一章](../extra-models.md#about-user_indict){.internal-link target=_blank}。 -`token` 端点的响应必须是一个 JSON 对象。 +## 返回 Token -它应该有一个 `token_type`。在我们的例子中,由于我们使用的是「Bearer」令牌,因此令牌类型应为「`bearer`」。 +`token` 端点的响应必须是 JSON 对象。 -并且还应该有一个 `access_token` 字段,它是一个包含我们的访问令牌的字符串。 +响应返回的内容应该包含 `token_type`。本例中用的是**Bearer**Token,因此, Token 类型应为**`bearer`**。 -对于这个简单的示例,我们将极其不安全地返回相同的 `username` 作为令牌。 +返回内容还应包含 `access_token` 字段,它是包含权限 Token 的字符串。 -!!! tip - 在下一章中,你将看到一个真实的安全实现,使用了哈希密码和 JWT 令牌。 +本例只是简单的演示,返回的 Token 就是 `username`,但这种方式极不安全。 - 但现在,让我们仅关注我们需要的特定细节。 +!!! tip "提示" + + 下一章介绍使用哈希密码和 JWT Token 的真正安全机制。 + + 但现在,仅关注所需的特定细节。 ```Python hl_lines="85" {!../../../docs_src/security/tutorial003.py!} ``` -!!! tip - 根据规范,你应该像本示例一样,返回一个带有 `access_token` 和 `token_type` 的 JSON。 +!!! tip "提示" - 这是你必须在代码中自行完成的工作,并且要确保使用了这些 JSON 字段。 + 按规范的要求,应像本示例一样,返回带有 `access_token` 和 `token_type` 的 JSON 对象。 - 这几乎是唯一的你需要自己记住并正确地执行以符合规范的事情。 + 这是开发者必须在代码中自行完成的工作,并且要确保使用这些 JSON 的键。 - 其余的,**FastAPI** 都会为你处理。 + 这几乎是唯一需要开发者牢记在心,并按规范要求正确执行的事。 + + **FastAPI** 则负责处理其它的工作。 ## 更新依赖项 -现在我们将更新我们的依赖项。 +接下来,更新依赖项。 -我们想要仅当此用户处于启用状态时才能获取 `current_user`。 +使之仅在当前用户为激活状态时,才能获取 `current_user`。 -因此,我们创建了一个额外的依赖项 `get_current_active_user`,而该依赖项又以 `get_current_user` 作为依赖项。 +为此,要再创建一个依赖项 `get_current_active_user`,此依赖项以 `get_current_user` 依赖项为基础。 -如果用户不存在或处于未启用状态,则这两个依赖项都将仅返回 HTTP 错误。 +如果用户不存在,或状态为未激活,这两个依赖项都会返回 HTTP 错误。 -因此,在我们的端点中,只有当用户存在,身份认证通过且处于启用状态时,我们才能获得该用户: +因此,在端点中,只有当用户存在、通过身份验证、且状态为激活时,才能获得该用户: ```Python hl_lines="58-67 69-72 90" {!../../../docs_src/security/tutorial003.py!} ``` -!!! info - 我们在此处返回的值为 `Bearer` 的额外响应头 `WWW-Authenticate` 也是规范的一部分。 +!!! info "说明" + + 此处返回值为 `Bearer` 的响应头 `WWW-Authenticate` 也是规范的一部分。 - 任何的 401「未认证」HTTP(错误)状态码都应该返回 `WWW-Authenticate` 响应头。 + 任何 401**UNAUTHORIZED**HTTP(错误)状态码都应返回 `WWW-Authenticate` 响应头。 - 对于 bearer 令牌(我们的例子),该响应头的值应为 `Bearer`。 + 本例中,因为使用的是 Bearer Token,该响应头的值应为 `Bearer`。 - 实际上你可以忽略这个额外的响应头,不会有什么问题。 + 实际上,忽略这个附加响应头,也不会有什么问题。 - 但此处提供了它以符合规范。 + 之所以在此提供这个附加响应头,是为了符合规范的要求。 - 而且,(现在或将来)可能会有工具期望得到并使用它,然后对你或你的用户有用处。 + 说不定什么时候,就有工具用得上它,而且,开发者或用户也可能用得上。 - 这就是遵循标准的好处... + 这就是遵循标准的好处…… ## 实际效果 -打开交互式文档:http://127.0.0.1:8000/docs。 +打开 API 文档:http://127.0.0.1:8000/docs。 -### 身份认证 +### 身份验证 -点击「Authorize」按钮。 +点击**Authorize**按钮。 使用以下凭证: @@ -216,15 +224,15 @@ UserInDB( -在系统中进行身份认证后,你将看到: +通过身份验证后,显示下图所示的内容: -### 获取本人的用户数据 +### 获取当前用户数据 -现在执行 `/users/me` 路径的 `GET` 操作。 +使用 `/users/me` 路径的 `GET` 操作。 -你将获得你的用户数据,如: +可以提取如下当前用户数据: ```JSON { @@ -238,7 +246,7 @@ UserInDB( -如果你点击锁定图标并注销,然后再次尝试同一操作,则会得到 HTTP 401 错误: +点击小锁图标,注销后,再执行同样的操作,则会得到 HTTP 401 错误: ```JSON { @@ -246,17 +254,17 @@ UserInDB( } ``` -### 未启用的用户 +### 未激活用户 -现在尝试使用未启用的用户,并通过以下方式进行身份认证: +测试未激活用户,输入以下信息,进行身份验证: 用户名:`alice` 密码:`secret2` -然后尝试执行 `/users/me` 路径的 `GET` 操作。 +然后,执行 `/users/me` 路径的 `GET` 操作。 -你将得到一个「未启用的用户」错误,如: +显示下列**未激活用户**错误信息: ```JSON { @@ -264,12 +272,12 @@ UserInDB( } ``` -## 总结 +## 小结 -现在你掌握了为你的 API 实现一个基于 `username` 和 `password` 的完整安全系统的工具。 +使用本章的工具实现基于 `username` 和 `password` 的完整 API 安全系统。 -使用这些工具,你可以使安全系统与任何数据库以及任何用户或数据模型兼容。 +这些工具让安全系统兼容任何数据库、用户及数据模型。 -唯一缺少的细节是它实际上还并不「安全」。 +唯一欠缺的是,它仍然不是真的**安全**。 -在下一章中,你将看到如何使用一个安全的哈希密码库和 JWT 令牌。 +下一章,介绍使用密码哈希支持库与 JWT 令牌实现真正的安全机制。