You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

7.2 KiB

安全性 - 入門

想像你有一個部署在某個網域的後端 API。

還有一個前端在另一個網域,或同一網域的不同路徑(或是行動應用程式)。

你希望前端能用使用者名稱與密碼向後端進行身分驗證。

我們可以用 OAuth2 搭配 FastAPI 來實作。

但不必通讀整份冗長規格只為了找出你需要的幾個重點。

就用 FastAPI 提供的工具處理安全性。

看起來如何

先直接跑範例看效果,再回頭理解其原理。

建立 main.py

將範例複製到檔案 main.py

{* ../../docs_src/security/tutorial001_an_py310.py *}

執行

/// info

當你使用 pip install "fastapi[standard]" 指令安裝時,python-multipart 套件會隨 FastAPI 自動安裝。

不過若只執行 pip install fastapi,預設不會包含 python-multipart

若要手動安裝,請先建立並啟用一個虛擬環境,接著執行:

$ pip install python-multipart

因為 OAuth2 會以「form data」傳送 usernamepassword

///

用以下指令執行範例:

$ fastapi dev

<span style="color: green;">INFO</span>:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)

檢查

開啟互動式文件:http://127.0.0.1:8000/docs

你會看到類似這樣:

/// check | Authorize 按鈕!

你會看到一個新的「Authorize」按鈕。

而你的「路徑操作」右上角也會出現一個小鎖頭可以點擊。

///

點擊後會跳出一個小視窗,讓你輸入 usernamepassword(以及其他可選欄位):

/// note | 注意

不管你在表單輸入什麼,現在都還不會成功;等等我們會把它完成。

///

這當然不是給最終使用者用的前端,但它是用來互動式文件化整個 API 的極佳自動化工具。

前端團隊(也可能就是你)可以使用它。

第三方應用或系統也能使用它。

你也能用它來除錯、檢查與測試同一個應用。

password 流程

現在回頭理解剛剛那些是什麼。

在 OAuth2 中,password 是處理安全與身分驗證的其中一種「流程」(flow)。

OAuth2 的設計讓後端或 API 可以獨立於執行使用者驗證的伺服器。

但在這個例子中,同一個 FastAPI 應用會同時處理 API 與驗證。

簡化來看流程如下:

  • 使用者在前端輸入 usernamepassword,按下 Enter
  • 前端(在使用者的瀏覽器中執行)把 usernamepassword 傳到我們 API 的特定 URL(在程式中宣告為 tokenUrl="token")。
  • API 檢查 usernamepassword,並回傳一個「token(權杖)」(我們還沒實作這部分)。
    • 「token(權杖)」就是一段字串,之後可用來識別並驗證此使用者。
    • 通常 token 會設定一段時間後失效。
      • 因此使用者之後需要重新登入。
      • 若 token 被竊取,風險也較低;它不像永遠有效的萬用鑰匙(多數情況下)。
  • 前端會暫存這個 token。
  • 使用者在前端點擊前往其他頁面/區段。
  • 前端需要再向 API 取得資料。
    • 但該端點需要驗證。
    • 因此為了向 API 驗證,請求會帶上一個 Authorization 標頭,值為 Bearer 加上 token。
    • 例如 token 是 foobar,則 Authorization 標頭內容為:Bearer foobar

FastAPI 的 OAuth2PasswordBearer

FastAPI 提供多層抽象的工具來實作這些安全機制。

本例將使用 OAuth2 的 Password 流程,並以 Bearer token 進行驗證;我們會用 OAuth2PasswordBearer 類別來完成。

/// info

「Bearer」token 不是唯一選項。

但對本例最合適。

通常對多數情境也足夠,除非你是 OAuth2 專家並確信有更適合你的選項。

在那種情況下,FastAPI 也提供相應工具讓你自行組合。

///

當我們建立 OAuth2PasswordBearer 類別的實例時,會傳入 tokenUrl 參數。這個參數包含了客戶端(在使用者瀏覽器中執行的前端)用來送出 usernamepassword 以取得 token 的 URL。

{* ../../docs_src/security/tutorial001_an_py310.py hl[8] *}

/// tip

這裡的 tokenUrl="token" 指的是尚未建立的相對 URL token。因為是相對 URL,所以等同於 ./token

由於使用了相對 URL,若你的 API 位於 https://example.com/,那它會指向 https://example.com/token;但若你的 API 位於 https://example.com/api/v1/,那它會指向 https://example.com/api/v1/token

使用相對 URL 很重要,能確保你的應用在像是在 Proxy 後方這類進階情境中仍能正常運作。

///

這個參數不會建立該端點/「路徑操作」,而是宣告 /token 將是客戶端用來取得 token 的 URL。這些資訊會出現在 OpenAPI,並被互動式 API 文件系統使用。

我們很快也會建立實際的路徑操作。

/// info

如果你是非常嚴格的「Pythonista」,可能不喜歡參數名稱用 tokenUrl 而不是 token_url

那是因為它沿用了 OpenAPI 規格中的名稱。如此一來,若你要深入查閱這些安全方案,便能直接複製貼上去搜尋更多資訊。

///

變數 oauth2_schemeOAuth2PasswordBearer 的實例,但同時它也是「可呼叫的」(callable)。

它可以這樣被呼叫:

oauth2_scheme(some, parameters)

因此它可以配合 Depends 使用。

如何使用

現在你可以在相依性中傳入 oauth2_schemeDepends 搭配。

{* ../../docs_src/security/tutorial001_an_py310.py hl[12] *}

此相依性會提供一個 str,指派給「路徑操作函式」的參數 token

FastAPI 會知道可以使用這個相依性,在 OpenAPI(以及自動產生的 API 文件)中定義一個「安全性方案」。

/// info | 技術細節

FastAPI 之所以知道可以用(相依性中宣告的)OAuth2PasswordBearer 類別,在 OpenAPI 中定義安全性方案,是因為它繼承自 fastapi.security.oauth2.OAuth2,而後者又繼承自 fastapi.security.base.SecurityBase

所有能與 OpenAPI(以及自動 API 文件)整合的安全工具都繼承自 SecurityBase,FastAPI 才能知道如何把它們整合進 OpenAPI。

///

它做了什麼

它會從請求中尋找 Authorization 標頭,檢查其值是否為 Bearer 加上一段 token,並將該 token 以 str 回傳。

若未找到 Authorization 標頭,或其值不是 Bearer token,則會直接回傳 401(UNAUTHORIZED)錯誤。

你不必再自行檢查 token 是否存在;你可以確信只要你的函式被執行,該 token 參數就一定會是 str

你可以在互動式文件中試試看:

我們還沒驗證 token 是否有效,但這已是個開始。

小結

只需多寫 3、4 行,就能有一個基本的安全機制。