-# 使用密码和 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` 等其它名称。
-ä¸\8dè¿\87ä¸\8dç\94¨æ\8b\85å¿\83ï¼\8cä½ å\8f¯ä»¥å\9c¨å\89\8d端æ\8c\89ç\85§ä½ ç\9a\84æ\83³æ³\95å°\86å®\83å±\95示ç»\99æ\9c\80ç»\88ç\94¨æ\88·。
+ä¸\8dè¿\87ä¹\9fä¸\8dç\94¨æ\8b\85å¿\83ï¼\8cå\89\8d端ä»\8då\8f¯ä»¥æ\98¾ç¤ºç»\88端ç\94¨æ\88·æ\89\80é\9c\80ç\9a\84å\90\8dç§°。
-而且你的数据库模型也可以使用你想用的任何其他名称。
+数据库模型也可以使用所需的名称。
-但是对于登录*路径操作*,我们需要使用这些名称来与规范兼容(以具备例如使用集成的 API 文档系统的能力)。
+但对于登录*路径操作*,则要使用兼容规范的 `username` 和 `password`,(例如,实现与 API 文档集成)。
-è§\84è\8c\83è¿\98å\86\99æ\98\8eäº\86 `username` å\92\8c `password` å¿\85é¡»ä½\9c为表å\8d\95æ\95°æ\8d®å\8f\91é\80\81ï¼\88å\9b æ¤ï¼\8cæ¤å¤\84ä¸\8dè\83½ä½¿ç\94¨ JSONï¼\89。
+该è§\84è\8c\83è¦\81æ±\82å¿\85须以表å\8d\95æ\95°æ\8d®å½¢å¼\8få\8f\91é\80\81 `username` å\92\8c `password`ï¼\8cå\9b æ¤ï¼\8cä¸\8dè\83½ä½¿ç\94¨ JSON 对象。
-### `scope`
+### `Scope`(作用域)
-规范还提到客户端可以发送另一个表单字段「`scope`」。
+OAuth2 还支持客户端发送**`scope`**表单字段。
-è¿\99个表å\8d\95å\97段ç\9a\84å\90\8d称为 `scope`ï¼\88å\8d\95æ\95°å½¢å¼\8fï¼\89ï¼\8cä½\86å®\9eé\99\85ä¸\8aå®\83æ\98¯ä¸\80个ç\94±ç©ºæ ¼å\88\86é\9a\94ç\9a\84ã\80\8cä½\9cç\94¨å\9f\9fã\80\8d组成的长字符串。
+è\99½ç\84¶è¡¨å\8d\95å\97段ç\9a\84å\90\8dç§°æ\98¯ `scope`ï¼\88å\8d\95æ\95°ï¼\89ï¼\8cä½\86å®\9eé\99\85ä¸\8aï¼\8cå®\83æ\98¯ä»¥ç©ºæ ¼å\88\86é\9a\94ç\9a\84ï¼\8cç\94±å¤\9a个**scope**组成的长字符串。
-每个「作用域」只是一个字符串(中间没有空格)。
+**作用域**只是不带空格的字符串。
-å®\83们é\80\9a常ç\94¨äº\8e声æ\98\8eç\89¹å®\9aç\9a\84安全权限,例如:
+常ç\94¨äº\8e声æ\98\8eæ\8c\87å®\9a安全权限,例如:
-* `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!}
### 校验密码
-目前我们已经从数据库中获取了用户数据,但尚未校验密码。
+至此,我们已经从数据库中获取了用户数据,但尚未校验密码。
-让我们首先将这些数据放入 Pydantic `UserInDB` 模型中。
+接下来,首先将数据放入 Pydantic 的 `UserInDB` 模型。
-æ°¸è¿\9cä¸\8dè¦\81ä¿\9då\98æ\98\8eæ\96\87å¯\86ç \81ï¼\8cå\9b æ¤ï¼\8cæ\88\91们å°\86使用(伪)哈希密码系统。
+注æ\84\8fï¼\9aæ°¸è¿\9cä¸\8dè¦\81ä¿\9då\98æ\98\8eæ\96\87å¯\86ç \81ï¼\8cæ\9c¬ä¾\8bæ\9a\82æ\97¶å\85\88使用(伪)哈希密码系统。
-如果密码不匹配,我们将返回同一个错误。
+如果密码不匹配,则返回与上面相同的错误。
-#### å\93\88å¸\8cå¯\86ç \81
+#### å¯\86ç \81å\93\88å¸\8c
-「哈希」的意思是:将某些内容(在本例中为密码)转换为看起来像乱码的字节序列(只是一个字符串)。
+**哈希**是指,将指定内容(本例中为密码)转换为形似乱码的字节序列(其实就是字符串)。
-æ¯\8fæ¬¡ä½ ä¼ å\85¥å®\8cå\85¨ç\9b¸å\90\8cç\9a\84å\86\85容ï¼\88å®\8cå\85¨ç\9b¸å\90\8cç\9a\84å¯\86ç \81ï¼\89æ\97¶ï¼\8cä½ é\83½ä¼\9aå¾\97å\88°完全相同的乱码。
+æ¯\8fæ¬¡ä¼ å\85¥å®\8cå\85¨ç\9b¸å\90\8cç\9a\84å\86\85容ï¼\88æ¯\94å¦\82ï¼\8cå®\8cå\85¨ç\9b¸å\90\8cç\9a\84å¯\86ç \81ï¼\89æ\97¶ï¼\8cå¾\97å\88°ç\9a\84é\83½æ\98¯完全相同的乱码。
-但是你不能从乱码转换回密码。
+但这个乱码无法转换回传入的密码。
-##### 为ä»\80ä¹\88使ç\94¨å\93\88å¸\8cå¯\86ç \81
+##### 为ä»\80ä¹\88使ç\94¨å¯\86ç \81å\93\88å¸\8c
-å¦\82æ\9e\9cä½ ç\9a\84æ\95°æ\8d®åº\93被ç\9b\97ï¼\8cå°\8få\81·å°\86æ\97 æ³\95è\8e·å¾\97ç\94¨æ\88·ç\9a\84æ\98\8eæ\96\87å¯\86ç \81ï¼\8cå\8fªæ\9c\89哈希值。
+å\8e\9få\9b å¾\88ç®\80å\8d\95ï¼\8cå\81\87å¦\82æ\95°æ\8d®åº\93被ç\9b\97ï¼\8cçª\83è´¼æ\97 æ³\95è\8e·å\8f\96ç\94¨æ\88·ç\9a\84æ\98\8eæ\96\87å¯\86ç \81ï¼\8cå¾\97å\88°ç\9a\84å\8fªæ\98¯哈希值。
-因此,小偷将无法尝试在另一个系统中使用这些相同的密码(由于许多用户在任何地方都使用相同的密码,因此这很危险)。
+这样一来,窃贼就无法在其它应用中使用窃取的密码,要知道,很多用户在所有系统中都使用相同的密码,风险超大。
```Python hl_lines="80-83"
{!../../../docs_src/security/tutorial003.py!}
#### 关于 `**user_dict`
-`UserInDB(**user_dict)` 表示:
+`UserInDB(**user_dict)` 是指:
-*直接将 `user_dict` 的键和值作为关键字参数传递,等同于:*
+*直接把 `user_dict` 的键与值当作关键字参数传递,等效于:*
```Python
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 对象。
-å¹¶ä¸\94è¿\98åº\94该æ\9c\89ä¸\80个 `access_token` å\97段ï¼\8cå®\83æ\98¯ä¸\80个å\8c\85å\90«æ\88\91们ç\9a\84访é\97®ä»¤ç\89\8cç\9a\84å\97符串。
+å\93\8dåº\94è¿\94å\9b\9eç\9a\84å\86\85容åº\94该å\8c\85å\90« `token_type`ã\80\82æ\9c¬ä¾\8bä¸ç\94¨ç\9a\84æ\98¯**Bearer**Tokenï¼\8cå\9b æ¤ï¼\8c Token ç±»å\9e\8båº\94为**`bearer`**。
-对于这个简单的示例,我们将极其不安全地返回相同的 `username` 作为令牌。
+返回内容还应包含 `access_token` 字段,它是包含权限 Token 的字符串。
-!!! tip
- 在下一章中,你将看到一个真实的安全实现,使用了哈希密码和 <abbr title="JSON Web Tokens">JWT</abbr> 令牌。
+本例只是简单的演示,返回的 Token 就是 `username`,但这种方式极不安全。
- 但现在,让我们仅关注我们需要的特定细节。
+!!! tip "提示"
+
+ 下一章介绍使用哈希密码和 <abbr title="JSON Web Tokens">JWT</abbr> 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`。
- 实际上你可以忽略这个额外的响应头,不会有什么问题。
+ 实际上,忽略这个附加响应头,也不会有什么问题。
- ä½\86æ¤å¤\84æ\8f\90ä¾\9bäº\86å®\83以符å\90\88è§\84è\8c\83。
+ ä¹\8bæ\89\80以å\9c¨æ¤æ\8f\90ä¾\9bè¿\99个é\99\84å\8a å\93\8dåº\94头ï¼\8cæ\98¯ä¸ºäº\86符å\90\88è§\84è\8c\83ç\9a\84è¦\81æ±\82。
- è\80\8cä¸\94ï¼\8cï¼\88ç\8e°å\9c¨æ\88\96å°\86æ\9d¥ï¼\89å\8f¯è\83½ä¼\9aæ\9c\89å·¥å\85·æ\9c\9fæ\9c\9bå¾\97å\88°å¹¶ä½¿ç\94¨å®\83ï¼\8cç\84¶å\90\8eå¯¹ä½ æ\88\96ä½ ç\9a\84ç\94¨æ\88·æ\9c\89ç\94¨å¤\84。
+ 说ä¸\8då®\9aä»\80ä¹\88æ\97¶å\80\99ï¼\8cå°±æ\9c\89å·¥å\85·ç\94¨å¾\97ä¸\8aå®\83ï¼\8cè\80\8cä¸\94ï¼\8cå¼\80å\8f\91è\80\85æ\88\96ç\94¨æ\88·ä¹\9få\8f¯è\83½ç\94¨å¾\97ä¸\8a。
- 这就是遵循标准的好处...
+ 这就是遵循标准的好处……
## 实际效果
-打开交互式文档:<a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>。
+打开 API 文档:<a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>。
-### 身份认证
+### 身份验证
-点击「Authorize」按钮。
+点击**Authorize**按钮。
使用以下凭证:
<img src="https://fastapi.tiangolo.com/img/tutorial/security/image04.png">
-在系统中进行身份认证后,你将看到:
+通过身份验证后,显示下图所示的内容:
<img src="https://fastapi.tiangolo.com/img/tutorial/security/image05.png">
-### 获取本人的用户数据
+### 获取当前用户数据
-现在执行 `/users/me` 路径的 `GET` 操作。
+使用 `/users/me` 路径的 `GET` 操作。
-你将获得你的用户数据,如:
+可以提取如下当前用户数据:
```JSON
{
<img src="https://fastapi.tiangolo.com/img/tutorial/security/image06.png">
-如果你点击锁定图标并注销,然后再次尝试同一操作,则会得到 HTTP 401 错误:
+点击小锁图标,注销后,再执行同样的操作,则会得到 HTTP 401 错误:
```JSON
{
}
```
-### 未启用的用户
+### 未激活用户
-现在尝试使用未启用的用户,并通过以下方式进行身份认证:
+测试未激活用户,输入以下信息,进行身份验证:
用户名:`alice`
密码:`secret2`
-然后尝试执行 `/users/me` 路径的 `GET` 操作。
+然后,执行 `/users/me` 路径的 `GET` 操作。
-你将得到一个「未启用的用户」错误,如:
+显示下列**未激活用户**错误信息:
```JSON
{
}
```
-## 总结
+## 小结
-现在你掌握了为你的 API 实现一个基于 `username` 和 `password` 的完整安全系统的工具。
+使用本章的工具实现基于 `username` 和 `password` 的完整 API 安全系统。
-使用这些工具,你可以使安全系统与任何数据库以及任何用户或数据模型兼容。
+这些工具让安全系统兼容任何数据库、用户及数据模型。
-唯一缺少的细节是它实际上还并不「安全」。
+唯一欠缺的是,它仍然不是真的**安全**。
-在下一章中,你将看到如何使用一个安全的哈希密码库和 <abbr title="JSON Web Tokens">JWT</abbr> 令牌。
+下一章,介绍使用密码哈希支持库与 <abbr title="JSON Web Tokens">JWT</abbr> 令牌实现真正的安全机制。