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

polarisxu

共 5440字,需浏览 11分钟

 · 2021-09-21

今天继续使用 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 也还是挺快的,你觉得呢?




往期推荐


我是 polarisxu,北大硕士毕业,曾在 360 等知名互联网公司工作,10多年技术研发与架构经验!2012 年接触 Go 语言并创建了 Go 语言中文网!著有《Go语言编程之旅》、开源图书《Go语言标准库》等。


坚持输出技术(包括 Go、Rust 等技术)、职场心得和创业感悟!欢迎关注「polarisxu」一起成长!也欢迎加我微信好友交流:gopherstudio

浏览 107
点赞
评论
收藏
分享

手机扫一扫分享

举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

举报