Rust 中的 JWT 实战:使用 jsonwebtoken 和 Salvo 构建安全的 Web 应用

文摘   2024-10-28 00:06   北京  

1. 什么是 JSON Web Token (JWT)?

JSON Web Token (JWT) 是一种开放标准(RFC 7519),用于在网络应用环境间安全地传输信息。JWT 通常用于身份验证和信息交换。它由三部分组成:头部(Header)、载荷(Payload)和签名(Signature),这三部分通过.连接在一起。

1.1 JWT 的结构

  • Header: 包含令牌的类型(JWT)和所使用的签名算法(如 HMAC SHA256 或 RSA)。
  • Payload: 包含声明(Claims),即关于实体(通常是用户)和其他数据的声明。
  • Signature: 用于验证消息在传输过程中没有被更改,并且对于使用私钥签名的令牌,还可以验证发送者的身份。

2. JWT 的工作原理

  1. 用户登录:用户提供凭据(如用户名和密码)进行身份验证。
  2. 服务器验证:服务器验证凭据,如果有效,则生成一个 JWT。
  3. 返回 JWT:服务器将 JWT 返回给客户端。
  4. 客户端存储 JWT:客户端通常将 JWT 存储在本地存储或 Cookie 中。
  5. 请求资源:客户端在后续请求中将 JWT 包含在请求头中(通常是Authorization头)。
  6. 服务器验证 JWT:服务器验证 JWT 的有效性,并根据 JWT 中的信息决定是否授予访问权限。

3. JWT 的优点

  • 无状态:JWT 是自包含的,服务器不需要存储会话信息。
  • 跨域支持:JWT 可以轻松地在不同域之间传递。
  • 安全性:通过签名和加密,JWT 可以防止篡改和伪造。

4. Rust 中的 JWT 实战示例

我们将使用 Rust 和jsonwebtoken库来实现一个简单的 JWT 身份验证系统,并使用Salvo 0.73框架来处理 HTTP 请求。

4.1 创建项目

首先,创建一个新的 Rust 项目并添加必要的依赖:

cargo new jwt_salvo_demo
cd jwt_salvo_demo

Cargo.toml文件中添加以下依赖:

[dependencies]
salvo = "0.73"
jsonwebtoken = "8.1"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
chrono = "0.4"

4.2 定义 JWT 载荷

创建一个src/models.rs文件,定义 JWT 载荷结构:

use serde::{Deserialize, Serialize};
use chrono::{DateTime, Utc};

#[derive(Debug, Serialize, Deserialize)]
pub struct Claims {
    pub sub: String// 用户ID
    pub exp: i64,    // 过期时间
    pub iat: i64,    // 签发时间
}

impl Claims {
    pub fn new(sub: String, exp: DateTime<Utc>) -> Self {
        Self {
            sub,
            exp: exp.timestamp(),
            iat: Utc::now().timestamp(),
        }
    }
}

4.3 实现 JWT 生成和验证

创建一个src/jwt.rs文件,实现 JWT 的生成和验证逻辑:

use jsonwebtoken::{encode, decode, Header, EncodingKey, DecodingKey, Validation, errors::Error};
use chrono::{Duration, Utc};
use crate::models::Claims;

const SECRET_KEY: &[u8] = b"your_secret_key";

pub fn generate_token(user_id: &str) -> Result<String, Error> {
    let claims = Claims::new(user_id.to_string(), Utc::now() + Duration::hours(1));
    encode(&Header::default(), &claims, &EncodingKey::from_secret(SECRET_KEY))
}

pub fn validate_token(token: &str) -> Result<Claims, Error> {
    decode::<Claims>(token, &DecodingKey::from_secret(SECRET_KEY), &Validation::default()).map(|data| data.claims)
}

4.4 实现 HTTP 路由

src/main.rs文件中,使用 Salvo 框架实现 HTTP 路由:

mod models;
mod jwt;

use salvo::prelude::*;
use salvo::http::StatusCode;
use serde::Deserialize;
use crate::jwt::{generate_token, validate_token};

#[derive(Deserialize)]
struct LoginRequest {
    username: String,
    password: String,
}

#[handler]
async fn login(req: &mut Request, res: &mut Response) {
    let login_req: LoginRequest = req.parse_json().await.unwrap();

    // 模拟用户验证
    if login_req.username == "user1" && login_req.password == "password1" {
        let token = generate_token(&login_req.username).unwrap();
        res.render(Json(json!({ "token": token })));
    } else {
        res.status_code(StatusCode::UNAUTHORIZED);
        res.render(Json(json!({ "message""Invalid credentials" })));
    }
}

#[handler]
async fn profile(req: &mut Request, res: &mut Response) {
    if let Some(token) = req.header("Authorization") {
        let token = token.split_whitespace().last().unwrap();
        match validate_token(token) {
            Ok(claims) => {
                res.render(Json(json!({ "user": claims })));
            }
            Err(_) => {
                res.status_code(StatusCode::FORBIDDEN);
                res.render(Json(json!({ "message""Invalid token" })));
            }
        }
    } else {
        res.status_code(StatusCode::UNAUTHORIZED);
        res.render(Json(json!({ "message""No token provided" })));
    }
}

#[tokio::main]
async fn main() {
    let router = Router::new()
        .push(Router::with_path("login").post(login))
        .push(Router::with_path("profile").get(profile));

    let acceptor = TcpListener::new("127.0.0.1:3000").bind().await;
    Server::new(acceptor).serve(router).await;
}

4.5 运行服务器

在终端中运行以下命令启动服务器:

cargo run

4.6 测试 JWT 系统

  1. 登录并获取 JWT

    使用 Postman 或 curl 发送 POST 请求到/login接口:

curl -X POST http://localhost:3000/login -H "Content-Type: application/json" -d '{"username": "user1", "password": "password1"}'

响应将包含一个 JWT:

{
   "token""eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyMSIsImV4cCI6MTYxNjIwMDAwMCwiaWF0IjoxNjE2MjAzNjAwfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
}
  1. 访问受保护的资源

    使用获取到的 JWT 访问/profile接口:

curl -X GET http://localhost:3000/profile -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyMSIsImV4cCI6MTYxNjIwMDAwMCwiaWF0IjoxNjE2MjAzNjAwfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"

响应将包含用户信息:

{
    "user": {
        "sub""user1",
        "exp"1616200000,
        "iat"1616203600
    }
}

5. 总结

通过本文,你已经了解了 JWT 的基本概念、工作原理以及如何在 Rust 中使用jsonwebtokenSalvo 0.73框架实现一个简单的 JWT 身份验证系统。JWT 是一种强大且灵活的身份验证机制,适用于各种 Web 应用场景。希望这篇入门指南能帮助你快速上手 JWT,并在实际项目中应用它。


6. 进阶阅读

  • JWT 安全性:了解如何保护 JWT 免受常见攻击,如重放攻击、XSS 和 CSRF。
  • JWT 刷新机制:学习如何实现 JWT 的刷新机制,以避免用户频繁登录。
  • JWT 与 OAuth2:探索 JWT 在 OAuth2 身份验证流程中的应用。

通过不断学习和实践,你将能够构建更加安全和高效的身份验证系统。


无论身在何处

有我不再孤单孤单

长按识别二维码关注我们




育儿之家 YEZJ
“Rust编程之道”,带你探索Rust语言之美,精进编程技艺,开启无限可能!🦀🦀🦀
 最新文章