引言:为什么在 Rust 中使用 OAuth 2.0 和 JWT?
现代网络应用程序很少能绕过身份验证和授权系统。OAuth 2.0 是用于委托访问的行业标准,而 JWT(JSON Web 令牌)是一种在各方之间传输声明的紧凑且安全的方式。Rust 凭借其高性能和内存安全性,在构建高负载服务器方面越来越受欢迎。在本文中,我们将通过实践,使用流行的 crate 和现代方法,讲解如何在 Rust 中实现带有 JWT 的 OAuth 2.0 授权。
1. 项目准备和依赖安装
首先,创建一个新的 Rust 项目并添加必要的依赖项。我们将使用 actix-web 作为网络服务器,jsonwebtoken 用于处理 JWT,serde 用于序列化,以及 reqwest 用于向外部 OAuth 提供商发送 HTTP 请求。
# Cargo.toml[package]name = "oauth-jwt-rust"version = "0.1.0"edition = "2021"
[dependencies]actix-web = "4"actix-rt = "2"jsonwebtoken = "9"serde = { version = "1.0", features = ["derive"] }serde_json = "1.0"reqwest = { version = "0.12", features = ["json"] }tokio = { version = "1", features = ["full"] }dotenv = "0.15"chrono = { version = "0.4", features = ["serde"] }uuid = { version = "1", features = ["v4"] }创建一个 .env 文件来存储密钥:
JWT_SECRET=your_super_secret_key_hereGITHUB_CLIENT_ID=your_github_client_idGITHUB_CLIENT_SECRET=your_github_client_secret2. JWT 令牌的基本结构
JWT 由三部分组成:头部、载荷和签名。让我们定义用于处理令牌的结构。
// src/jwt.rsuse chrono::{Utc, Duration};use jsonwebtoken::{encode, decode, Header, Validation, EncodingKey, DecodingKey};use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]pub struct Claims { pub sub: String, // 用户标识符 pub email: String, // 用户邮箱 pub exp: usize, // 过期时间 pub iat: usize, // 签发时间}
pub fn create_jwt(user_id: &str, email: &str) -> Result { let now = Utc::now(); let expiration = now + Duration::hours(24); // 令牌有效期为 24 小时
let claims = Claims { sub: user_id.to_string(), email: email.to_string(), exp: expiration.timestamp() as usize, iat: now.timestamp() as usize, };
let secret = std::env::var("JWT_SECRET").expect("JWT_SECRET must be set"); encode( &Header::default(), &claims, &EncodingKey::from_secret(secret.as_bytes()), )}
pub fn validate_jwt(token: &str) -> Result { let secret = std::env::var("JWT_SECRET").expect("JWT_SECRET must be set"); let token_data = decode::( token, &DecodingKey::from_secret(secret.as_bytes()), &Validation::default(), )?; Ok(token_data.claims)} 3. 使用 GitHub 实现 OAuth 2.0
让我们考虑经典的 OAuth 2.0 场景——通过 GitHub 进行授权。用户被重定向到 GitHub,确认后获得一个代码,我们用该代码交换访问令牌。
3.1. 启动 OAuth 流程的端点
// src/oauth.rsuse actix_web::{web, HttpResponse, HttpRequest};use reqwest::Client;use serde_json::Value;
pub async fn github_login() -> HttpResponse { let client_id = std::env::var("GITHUB_CLIENT_ID").unwrap(); let redirect_uri = "http://localhost:8080/auth/github/callback"; let auth_url = format!( "https://github.com/login/oauth/authorize?client_id={}&redirect_uri={}&scope=user:email", client_id, redirect_uri ); HttpRes