用 Rust 構建 API 系列教程:第三部分

關注「Rust 編程指北」,一起學習 Rust,給未來投資

大家好,我是胖蟹哥。

今天繼續使用 Rust 構建 API,第三部分,也是最後一部分。

這部分,我將解釋如何使用 JWT (JSON Web Token) 保護 POST /items 端點。

同樣,我把所有代碼放在同一個文件 (src/main.rs) 中,實際項目你不應該這麼做。

準備

我們需要在 .env 文件中增加一個新的環境變量。打開 .env 文件,然後在 MONGODB_URI 下方添加一個名爲 JWT_SECRET 的新變量,並將值設置爲你喜歡的任何值。

添加新的依賴項

我們需要添加一個新的依賴:jsonwebtoken

jsonwebtoken 是一個允許我們對 JWTs 進行編碼、解碼和驗證的 crate。

用新的依賴項更新 Cargo.toml 文件。

[dependencies]
tide = "0.16"
async-std = { version = "1", features = ["attributes"] }
serde = { version = "1", features = ["derive"] }
dotenv = "0.15"
mongodb = { version = "1", features = ["async-std-runtime"], default-features = false }
jsonwebtoken = "7"

開始編碼

We need to make some modifications to the code. First update the imports with the following

我們需要對代碼進行一些修改。首先是導入部分:

use async_std::stream::StreamExt;
use dotenv::dotenv;
use jsonwebtoken;
use mongodb::bson::doc;
use serde::{Deserialize, Serialize};
use std::{env, future::Future, pin::Pin};
use tide::{Body, Next, Request, Response, StatusCode};

這些都是我們將使用到的模塊。

我們需要創建一個新的結構體來定義 JWT 的有效負載(payload)。我不會在有效負載中添加任何內容,所以它只是一個空結構體。

src/main.rs 文件中添加以下內容:

#[derive(Serialize, Deserialize)]
struct TokenClaims {}

現在我們有了 TokenClaims,我們需要編寫中間件函數。加到 src/main.rs 中:

fn auth_middleware<'a>(
  req: Request<State>,
  next: Next<'a, State>,
) -> Pin<Box<dyn Future<Output = tide::Result> + Send + 'a>> {
  return Box::pin(async {
    // Retrieve the "Authorization" header from the request
    let authorization_header = req.header("Authorization");

    // Check that the "Authorization" header is not missing
    // if it is missing, respond with 401
    let authorization_header = match authorization_header {
      Some(h) => h.as_str(),
      None ={
        return Ok(Response::new(StatusCode::Unauthorized));
      }
    };

    // Attempt to remove the "Bearer " prefix
    // if it does not start with "Bearer " respond with 401
    let token = match authorization_header.strip_prefix("Bearer ") {
      Some(t) => t,
      None ={
        return Ok(Response::new(StatusCode::Unauthorized));
      }
    };

    // Retrieve the "JWT_SECRET" environment variable
    let secret = env::var("JWT_SECRET").unwrap();

    // Decode the JWT
    let token = jsonwebtoken::decode::<TokenClaims>(
      token,
      &jsonwebtoken::DecodingKey::from_secret(secret.as_ref()),
      // Do not require the "exp" claim in the token payload
      &jsonwebtoken::Validation {
        validate_exp: false,
        ..Default::default()
      },
    );

    // If there was an error when decoding the token respond with 401
    if token.is_err() {
      return Ok(Response::new(StatusCode::Unauthorized));
    }

    // The request is authorized, proceed with the next controller
    return Ok(next.run(req).await);
  });
}

注意上面的註釋。

總之,中間件檢查請求中是否有一個 Authorization 頭。然後它嘗試從頭部獲取 JWT。最後,使用 jsonwebtoken crate,嘗試解碼 JWT。如果在處理過程中出現任何錯誤,該函數將使用 401 HTTP 狀態碼對未經授權進行響應。否則,請求被授權,中間件運行鏈中的下一個控制器。

我們必須修改主函數來使用這個新的中間件,像這樣更新 POST /items 路由:

app.at("/items").with(auth_middleware).post(post_item);

啓動服務器測試下:

cargo run

如果你想發送一個 POST 請求到 /items ( http://localhost:8080/items ) ,你會得到一個 401 狀態碼。

你需要使用在 .env 文件中設置的祕鑰生成一個 JWT。一個簡單的方法是轉到 jwt.io,將有效負載更改爲空 JSON 對象 {} ,並用自己的方法替換默認的 secret。然後你可以複製這個 token。

在發送請求之前,請確保添加一個  Authorization 頭,其值爲 Bearer YOUR_JWT_TOKEN。當然,用實際的令牌替換 YOUR_JWT_TOKEN

現在,如果你嘗試再次發送 POST 請求,你應該得到一個 200 狀態代碼!

完結

本系列教程就結束了。

沒有涉及到很多複雜的內容,但講解了 Web API 涉及到的一些基本知識。通過框架,Rust 開發 API 也還是挺快的,你覺得呢?

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/jtmkEdlMxqI-RQWBoqx1bw