OAuth 客户端用于 Rust:oauth2-rs 库

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

oauth2-rs 库:在 Rust 上实现 OAuth 2.0



在现代 Web 开发中,OAuth 2.0 是用于委托访问的标准协议。oauth2-rs 库提供了用 Rust 语言实现的完整 OAuth 2.0 客户端。它允许集成通过流行服务的身份验证:Google、GitHub、Yandex、VK 等。



该库支持所有关键的授权流程:授权码、客户端凭证、隐式授权以及刷新令牌。特别关注安全性:对公共客户端使用 PKCE(代码交换证明密钥)和强制令牌验证。



安装 oauth2-rs



在 Cargo.toml 中添加依赖项:



[dependencies]oauth2 = "4.4"tokio = { version = "1", features = ["full"] }reqwest = { version = "0.11", features = ["json"] }serde = { version = "1.0", features = ["derive"] }


还需要 HTTP 客户端(推荐使用支持异步的 reqwest)和用于处理 JSON 的序列化器 serde。



OAuth 客户端的基本配置



我们来看一个 Google OAuth 2.0 的示例。首先创建配置:



use oauth2::{    AuthorizationCode, AuthUrl, ClientId, ClientSecret,     RedirectUrl, TokenUrl, CsrfToken, Scope,    basic::BasicClient, TokenResponse};

let client_id = ClientId::new("YOUR_GOOGLE_CLIENT_ID".to_string());let client_secret = ClientSecret::new("YOUR_GOOGLE_CLIENT_SECRET".to_string());let auth_url = AuthUrl::new("https://accounts.google.com/o/oauth2/v2/auth".to_string()) .expect("Invalid auth URL");let token_url = TokenUrl::new("https://oauth2.googleapis.com/token".to_string()) .expect("Invalid token URL");

let client = BasicClient::new( client_id, Some(client_secret), auth_url, Some(token_url)).set_redirect_uri( RedirectUrl::new("http://localhost:8080/callback".to_string()) .expect("Invalid redirect URL"));


生成授权 URL



创建需要将用户重定向到的链接:



let (auth_url, csrf_token) = client    .authorize_url(CsrfToken::new_random)    .add_scope(Scope::new("openid".to_string()))    .add_scope(Scope::new("email".to_string()))    .url();

println!("请点击链接:{}", auth_url);// 保存 csrf_token 以便在回调时验证


CsrfToken 可防止跨站请求伪造(CSRF)攻击。需要临时保存它(例如在会话或 Cookie 中),并在处理回调时进行验证。



处理回调并获取令牌



用户确认访问后,服务会将其重定向到带有 code 参数的 redirect_uri。以下是 Rust 处理程序的示例:



use actix_web::{web, HttpRequest, HttpResponse};use oauth2::AuthorizationCode;

async fn callback(query: web::Query<std::collections::HashMap<String, String>>) -> HttpResponse { let code = query.get("code") .expect("缺少授权码"); let state = query.get("state") .expect("缺少状态参数");

// 检查 state 是否与保存的 csrf_token 匹配 // ...

let token_result = client .exchange_code(AuthorizationCode::new(code.clone())) .request_async(&reqwest::Client::new()) .await;

match token_result { Ok(token) => { let access_token = token.access_token().secret(); let refresh_token = token.refresh_token() .map(|rt| rt.secret().clone()); // 将令牌保存到数据库或会话中 HttpResponse::Ok().body("授权成功!") }, Err(e) => { eprintln!("交换代码时出错:{:?}", e); HttpResponse::InternalServer

推荐