Browse Source

Merge branch 'master' into feat/exception-on-duplicate-path

pull/5595/head
Irfanuddin Shafi Ahmed 2 years ago
committed by GitHub
parent
commit
763bc792ff
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      docs/en/docs/release-notes.md
  2. 200
      docs/ja/docs/deployment/https.md
  3. 17
      docs/ko/docs/deployment/cloud.md
  4. 2
      docs/ru/docs/tutorial/body-fields.md
  5. 214
      docs/zh/docs/tutorial/security/simple-oauth2.md

4
docs/en/docs/release-notes.md

@ -2,6 +2,10 @@
## Latest Changes
* 🌐 Update Chinese translation for `docs/tutorial/security/simple-oauth2.md`. PR [#3844](https://github.com/tiangolo/fastapi/pull/3844) by [@jaystone776](https://github.com/jaystone776).
* 🌐 Add Korean translation for `docs/ko/docs/deployment/cloud.md`. PR [#10191](https://github.com/tiangolo/fastapi/pull/10191) by [@Sion99](https://github.com/Sion99).
* 🌐 Add Japanese translation for `docs/ja/docs/deployment/https.md`. PR [#10298](https://github.com/tiangolo/fastapi/pull/10298) by [@tamtam-fitness](https://github.com/tamtam-fitness).
* 🌐 Fix typo in Russian translation for `docs/ru/docs/tutorial/body-fields.md`. PR [#10224](https://github.com/tiangolo/fastapi/pull/10224) by [@AlertRED](https://github.com/AlertRED).
* 🌐 Add Polish translation for `docs/pl/docs/help-fastapi.md`. PR [#10121](https://github.com/tiangolo/fastapi/pull/10121) by [@romabozhanovgithub](https://github.com/romabozhanovgithub).
* 🌐 Add Russian translation for `docs/ru/docs/tutorial/header-params.md`. PR [#10226](https://github.com/tiangolo/fastapi/pull/10226) by [@AlertRED](https://github.com/AlertRED).
* 🌐 Add Chinese translation for `docs/zh/docs/deployment/versions.md`. PR [#10276](https://github.com/tiangolo/fastapi/pull/10276) by [@xzmeng](https://github.com/xzmeng).

200
docs/ja/docs/deployment/https.md

@ -0,0 +1,200 @@
# HTTPS について
HTTPSは単に「有効」か「無効」かで決まるものだと思いがちです。
しかし、それよりもはるかに複雑です。
!!! tip
もし急いでいたり、HTTPSの仕組みについて気にしないのであれば、次のセクションに進み、さまざまなテクニックを使ってすべてをセットアップするステップ・バイ・ステップの手順をご覧ください。
利用者の視点から **HTTPS の基本を学ぶ**に当たっては、次のリソースをオススメします: <a href="https://howhttps.works/" class="external-link" target="_blank">https://howhttps.works/</a>.
さて、**開発者の視点**から、HTTPSについて考える際に念頭に置くべきことをいくつかみていきましょう:
* HTTPSの場合、**サーバ**は**第三者**によって生成された**「証明書」を持つ**必要があります。
* これらの証明書は「生成」されたものではなく、実際には第三者から**取得**されたものです。
* 証明書には**有効期限**があります。
* つまりいずれ失効します。
* そのため**更新**をし、第三者から**再度取得**する必要があります。
* 接続の暗号化は**TCPレベル**で行われます。
* それは**HTTPの1つ下**のレイヤーです。
* つまり、**証明書と暗号化**の処理は、**HTTPの前**に行われます。
* **TCPは "ドメイン "について知りません**。IPアドレスについてのみ知っています。
* 要求された**特定のドメイン**に関する情報は、**HTTPデータ**に入ります。
* **HTTPS証明書**は、**特定のドメイン**を「証明」しますが、プロトコルと暗号化はTCPレベルで行われ、どのドメインが扱われているかを**知る前**に行われます。
* **デフォルトでは**、**IPアドレスごとに1つのHTTPS証明書**しか持てないことになります。
* これは、サーバーの規模やアプリケーションの規模に寄りません。
* しかし、これには**解決策**があります。
* **TLS**プロトコル(HTTPの前に、TCPレベルで暗号化を処理するもの)には、**<a href="https://en.wikipedia.org/wiki/Server_Name_Indication" class="external-link" target="_blank"><abbr title="サーバー名表示">SNI</abbr></a>**と呼ばれる**拡張**があります。
* このSNI拡張機能により、1つのサーバー(**単一のIPアドレス**を持つ)が**複数のHTTPS証明書**を持ち、**複数のHTTPSドメイン/アプリケーション**にサービスを提供できるようになります。
* これが機能するためには、**パブリックIPアドレス**でリッスンしている、サーバー上で動作している**単一の**コンポーネント(プログラム)が、サーバー内の**すべてのHTTPS証明書**を持っている必要があります。
* セキュアな接続を取得した**後**でも、通信プロトコルは**HTTPのまま**です。
* コンテンツは**HTTPプロトコル**で送信されているにもかかわらず、**暗号化**されています。
サーバー(マシン、ホストなど)上で**1つのプログラム/HTTPサーバー**を実行させ、**HTTPSに関する全てのこと**を管理するのが一般的です。
**暗号化された HTTPS リクエスト** を受信し、**復号化された HTTP リクエスト** を同じサーバーで実行されている実際の HTTP アプリケーション(この場合は **FastAPI** アプリケーション)に送信し、アプリケーションから **HTTP レスポンス** を受け取り、適切な **HTTPS 証明書** を使用して **暗号化** し、そして**HTTPS** を使用してクライアントに送り返します。
このサーバーはしばしば **<a href="https://en.wikipedia.org/wiki/TLS_termination_proxy" class="external-link" target="_blank">TLS Termination Proxy</a>**と呼ばれます。
TLS Termination Proxyとして使えるオプションには、以下のようなものがあります:
* Traefik(証明書の更新も対応)
* Caddy (証明書の更新も対応)
* Nginx
* HAProxy
## Let's Encrypt
Let's Encrypt以前は、これらの**HTTPS証明書**は信頼できる第三者によって販売されていました。
これらの証明書を取得するための手続きは面倒で、かなりの書類を必要とし、証明書はかなり高価なものでした。
しかしその後、**<a href="https://letsencrypt.org/" class="external-link" target="_blank">Let's Encrypt</a>** が作られました。
これはLinux Foundationのプロジェクトから生まれたものです。 自動化された方法で、**HTTPS証明書を無料で**提供します。これらの証明書は、すべての標準的な暗号化セキュリティを使用し、また短命(約3ヶ月)ですが、こういった寿命の短さによって、**セキュリティは実際に優れています**。
ドメインは安全に検証され、証明書は自動的に生成されます。また、証明書の更新も自動化されます。
このアイデアは、これらの証明書の取得と更新を自動化することで、**安全なHTTPSを、無料で、永遠に**利用できるようにすることです。
## 開発者のための HTTPS
ここでは、HTTPS APIがどのように見えるかの例を、主に開発者にとって重要なアイデアに注意を払いながら、ステップ・バイ・ステップで説明します。
### ドメイン名
ステップの初めは、**ドメイン名**を**取得すること**から始まるでしょう。その後、DNSサーバー(おそらく同じクラウドプロバイダー)に設定します。
おそらくクラウドサーバー(仮想マシン)かそれに類するものを手に入れ、<abbr title="変わらない">固定の</abbr> **パブリックIPアドレス**を持つことになるでしょう。
DNSサーバーでは、**取得したドメイン**をあなたのサーバーのパプリック**IPアドレス**に向けるレコード(「`Aレコード`」)を設定します。
これはおそらく、最初の1回だけあり、すべてをセットアップするときに行うでしょう。
!!! tip
ドメイン名の話はHTTPSに関する話のはるか前にありますが、すべてがドメインとIPアドレスに依存するため、ここで言及する価値があります。
### DNS
では、実際のHTTPSの部分に注目してみよう。
まず、ブラウザは**DNSサーバー**に**ドメインに対するIP**が何であるかを確認します。今回は、`someapp.example.com`とします。
DNSサーバーは、ブラウザに特定の**IPアドレス**を使用するように指示します。このIPアドレスは、DNSサーバーで設定した、あなたのサーバーが使用するパブリックIPアドレスになります。
<img src="/img/deployment/https/https01.svg">
### TLS Handshake の開始
ブラウザはIPアドレスと**ポート443**(HTTPSポート)で通信します。
通信の最初の部分は、クライアントとサーバー間の接続を確立し、使用する暗号鍵などを決めるだけです。
<img src="/img/deployment/https/https02.svg">
TLS接続を確立するためのクライアントとサーバー間のこのやりとりは、**TLSハンドシェイク**と呼ばれます。
### SNI拡張機能付きのTLS
サーバー内の**1つのプロセス**だけが、特定 の**IPアドレス**の特定の**ポート** で待ち受けることができます。
同じIPアドレスの他のポートで他のプロセスがリッスンしている可能性もありますが、IPアドレスとポートの組み合わせごとに1つだけです。
TLS(HTTPS)はデフォルトで`443`という特定のポートを使用する。つまり、これが必要なポートです。
このポートをリッスンできるのは1つのプロセスだけなので、これを実行するプロセスは**TLS Termination Proxy**となります。
TLS Termination Proxyは、1つ以上の**TLS証明書**(HTTPS証明書)にアクセスできます。
前述した**SNI拡張機能**を使用して、TLS Termination Proxy は、利用可能なTLS (HTTPS)証明書のどれを接続先として使用すべきかをチェックし、クライアントが期待するドメインに一致するものを使用します。
今回は、`someapp.example.com`の証明書を使うことになります。
<img src="/img/deployment/https/https03.svg">
クライアントは、そのTLS証明書を生成したエンティティ(この場合はLet's Encryptですが、これについては後述します)をすでに**信頼**しているため、その証明書が有効であることを**検証**することができます。
次に証明書を使用して、クライアントとTLS Termination Proxy は、 **TCP通信**の残りを**どのように暗号化するかを決定**します。これで**TLSハンドシェイク**の部分が完了します。
この後、クライアントとサーバーは**暗号化されたTCP接続**を持ちます。そして、その接続を使って実際の**HTTP通信**を開始することができます。
これが**HTTPS**であり、純粋な(暗号化されていない)TCP接続ではなく、**セキュアなTLS接続**の中に**HTTP**があるだけです。
!!! tip
通信の暗号化は、HTTPレベルではなく、**TCPレベル**で行われることに注意してください。
### HTTPS リクエスト
これでクライアントとサーバー(具体的にはブラウザとTLS Termination Proxy)は**暗号化されたTCP接続**を持つことになり、**HTTP通信**を開始することができます。
そこで、クライアントは**HTTPSリクエスト**を送信します。これは、暗号化されたTLSコネクションを介した単なるHTTPリクエストです。
<img src="/img/deployment/https/https04.svg">
### リクエストの復号化
TLS Termination Proxy は、合意が取れている暗号化を使用して、**リクエストを復号化**し、**プレーン (復号化された) HTTP リクエスト** をアプリケーションを実行しているプロセス (例えば、FastAPI アプリケーションを実行している Uvicorn を持つプロセス) に送信します。
<img src="/img/deployment/https/https05.svg">
### HTTP レスポンス
アプリケーションはリクエストを処理し、**プレーン(暗号化されていない)HTTPレスポンス** をTLS Termination Proxyに送信します。
<img src="/img/deployment/https/https06.svg">
### HTTPS レスポンス
TLS Termination Proxyは次に、事前に合意が取れている暗号(`someapp.example.com`の証明書から始まる)を使って**レスポンスを暗号化し**、ブラウザに送り返す。
その後ブラウザでは、レスポンスが有効で正しい暗号キーで暗号化されていることなどを検証します。そして、ブラウザはレスポンスを**復号化**して処理します。
<img src="/img/deployment/https/https07.svg">
クライアント(ブラウザ)は、レスポンスが正しいサーバーから来たことを知ることができます。 なぜなら、そのサーバーは、以前に**HTTPS証明書**を使って合意した暗号を使っているからです。
### 複数のアプリケーション
同じサーバー(または複数のサーバー)に、例えば他のAPIプログラムやデータベースなど、**複数のアプリケーション**が存在する可能性があります。
特定のIPとポート(この例ではTLS Termination Proxy)を扱うことができるのは1つのプロセスだけですが、他のアプリケーション/プロセスも、同じ**パブリックIPとポート**の組み合わせを使用しようとしない限り、サーバー上で実行することができます。
<img src="/img/deployment/https/https08.svg">
そうすれば、TLS Termination Proxy は、**複数のドメイン**や複数のアプリケーションのHTTPSと証明書を処理し、それぞれのケースで適切なアプリケーションにリクエストを送信することができます。
### 証明書の更新
将来のある時点で、各証明書は(取得後約3ヶ月で)**失効**します。
その後、Let's Encryptと通信する別のプログラム(別のプログラムである場合もあれば、同じTLS Termination Proxyである場合もある)によって、証明書を更新します。
<img src="/img/deployment/https/https.svg">
**TLS証明書**は、IPアドレスではなく、**ドメイン名に関連付けられて**います。
したがって、証明書を更新するために、更新プログラムは、認証局(Let's Encrypt)に対して、**そのドメインが本当に「所有」し、管理している**ことを**証明**する必要があります。
そのために、またさまざまなアプリケーションのニーズに対応するために、いくつかの方法があります。よく使われる方法としては:
* **いくつかのDNSレコードを修正します。**
* これをするためには、更新プログラムはDNSプロバイダーのAPIをサポートする必要があります。したがって、使用しているDNSプロバイダーによっては、このオプションが使える場合もあれば、使えない場合もあります。
* ドメインに関連付けられたパブリックIPアドレス上で、(少なくとも証明書取得プロセス中は)**サーバー**として実行します。
* 上で述べたように、特定のIPとポートでリッスンできるプロセスは1つだけです。
* これは、同じTLS Termination Proxyが証明書の更新処理も行う場合に非常に便利な理由の1つです。
* そうでなければ、TLS Termination Proxyを一時的に停止し、証明書を取得するために更新プログラムを起動し、TLS Termination Proxyで証明書を設定し、TLS Termination Proxyを再起動しなければならないかもしれません。TLS Termination Proxyが停止している間はアプリが利用できなくなるため、これは理想的ではありません。
アプリを提供しながらこのような更新処理を行うことは、アプリケーション・サーバー(Uvicornなど)でTLS証明書を直接使用するのではなく、TLS Termination Proxyを使用して**HTTPSを処理する別のシステム**を用意したくなる主な理由の1つです。
## まとめ
**HTTPS**を持つことは非常に重要であり、ほとんどの場合、かなり**クリティカル**です。開発者として HTTPS に関わる労力のほとんどは、これらの**概念とその仕組みを理解する**ことです。
しかし、ひとたび**開発者向けHTTPS**の基本的な情報を知れば、簡単な方法ですべてを管理するために、さまざまなツールを組み合わせて設定することができます。
次の章では、**FastAPI** アプリケーションのために **HTTPS** をセットアップする方法について、いくつかの具体例を紹介します。🔒

17
docs/ko/docs/deployment/cloud.md

@ -0,0 +1,17 @@
# FastAPI를 클라우드 제공업체에서 배포하기
사실상 거의 **모든 클라우드 제공업체**를 사용하여 여러분의 FastAPI 애플리케이션을 배포할 수 있습니다.
대부분의 경우, 주요 클라우드 제공업체에서는 FastAPI를 배포할 수 있도록 가이드를 제공합니다.
## 클라우드 제공업체 - 후원자들
몇몇 클라우드 제공업체들은 [**FastAPI를 후원하며**](../help-fastapi.md#sponsor-the-author){.internal-link target=_blank} ✨, 이를 통해 FastAPI와 FastAPI **생태계**가 지속적이고 건전한 **발전**을 할 수 있습니다.
이는 FastAPI와 **커뮤니티** (여러분)에 대한 진정한 헌신을 보여줍니다. 그들은 여러분에게 **좋은 서비스**를 제공할 뿐 만이 아니라 여러분이 **훌륭하고 건강한 프레임워크인** FastAPI 를 사용하길 원하기 때문입니다. 🙇
아래와 같은 서비스를 사용해보고 각 서비스의 가이드를 따를 수도 있습니다:
* <a href="https://docs.platform.sh/languages/python.html?utm_source=fastapi-signup&utm_medium=banner&utm_campaign=FastAPI-signup-June-2023" class="external-link" target="_blank">Platform.sh</a>
* <a href="https://docs.porter.run/language-specific-guides/fastapi" class="external-link" target="_blank">Porter</a>
* <a href="https://www.deta.sh/?ref=fastapi" class="external-link" target="_blank">Deta</a>

2
docs/ru/docs/tutorial/body-fields.md

@ -37,7 +37,7 @@
{!> ../../../docs_src/body_fields/tutorial001.py!}
```
Функция `Field` работает так же, как `Query`, `Path` и `Body`, у ее такие же параметры и т.д.
Функция `Field` работает так же, как `Query`, `Path` и `Body`, у неё такие же параметры и т.д.
!!! note "Технические детали"
На самом деле, `Query`, `Path` и другие функции, которые вы увидите в дальнейшем, создают объекты подклассов общего класса `Param`, который сам по себе является подклассом `FieldInfo` из Pydantic.

214
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
在下一章中,你将看到一个真实的安全实现,使用了哈希密码和 <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`
实际上你可以忽略这个额外的响应头,不会有什么问题。
实际上,忽略这个附加响应头,也不会有什么问题。
但此处提供了它以符合规范
之所以在此提供这个附加响应头,是为了符合规范的要求
而且,(现在或将来)可能会有工具期望得到并使用它,然后对你或你的用户有用处
说不定什么时候,就有工具用得上它,而且,开发者或用户也可能用得上
这就是遵循标准的好处...
这就是遵循标准的好处……
## 实际效果
打开交互式文档:<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**按钮。
使用以下凭证:
@ -216,15 +224,15 @@ UserInDB(
<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
{
@ -238,7 +246,7 @@ UserInDB(
<img src="https://fastapi.tiangolo.com/img/tutorial/security/image06.png">
如果你点击锁定图标并注销,然后再次尝试同一操作,则会得到 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 安全系统。
使用这些工具,你可以使安全系统与任何数据库以及任何用户或数据模型兼容
这些工具让安全系统兼容任何数据库、用户及数据模型
唯一缺少的细节是它实际上还并不「安全」
唯一欠缺的是,它仍然不是真的**安全**
在下一章中,你将看到如何使用一个安全的哈希密码库和 <abbr title="JSON Web Tokens">JWT</abbr> 令牌。
下一章,介绍使用密码哈希支持库与 <abbr title="JSON Web Tokens">JWT</abbr> 令牌实现真正的安全机制

Loading…
Cancel
Save