cargo.toml
name = "learn"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
otpauth = "0.4.1"
chrono = "0.4"
axum = "0.5"
serde_derive = "1.0"
serde_json = "1.0"
tokio = { version = "1", features = ["full"] }
serde = { version = "1.0", features = ["derive"] }
main.rs
#[derive(Deserialize)]
struct QueryParams {
user_otp: Option<String>, // 修改字段类型,
}
use otpauth::TOTP;
use chrono::Local;
use axum::{
http::StatusCode,
routing::get,
Router,
response::{IntoResponse,Html},
extract::Query,
};
use std::net::SocketAddr;
use tokio;
use serde::Deserialize;
async fn generate_otp(Query(params): Query<QueryParams>) -> impl IntoResponse{
// 检查 user_otp 是否存在且不为空,as_ref() 方法将 Option<T> 转换为 Option<&T>,即从包含值的 Option 中获取一个值的引用,而不是获取值本身。这样做的好处是避免了所有权的转移,允许在之后的链式调用中继续使用原始的 Option。
// map_or(default, f) 方法是 Option 类型的一个方法,它接受两个参数:default 和一个函数 f。如果 Option 是 Some,它会应用函数 f 到 Option 中包含的值上,并返回函数的结果。如果 Option 是 None,它会返回 default 值。
// default 是当 Option 为 None 时返回的默认值。
// f 是一个函数,当 Option 为 Some 时,这个函数会被应用到 Some 中的值上。
if params.user_otp.as_ref().map_or(true, String::is_empty) {
return (
StatusCode::BAD_REQUEST,
Html("<h1>user_opt is required</h1>".to_string())
)
.into_response();
}
let totp = TOTP::new("google");
// 假设手动构建 Key URL(根据实际情况调整)
let issuer = "baidu.com";
let account_name = "toms@baidu.com";
let key_url = format!("otpauth://totp/{}:{}?secret={}&issuer={}", issuer, account_name, "google", issuer);
println!("Key URL: {}", key_url);
// 调整 generate 方法的调用(根据实际方法签名调整)
let now = Local::now().timestamp();
let mut otps_html = String::new();
let passcode = totp.generate( 6,now as u64); // 假设这是正确的调用方式
let passcode_six_digits = ensure_six_digits(passcode.to_string());
println!("OTP: {}", passcode_six_digits);
for i in 0..10 {
let passcode = totp.generate( 6,now as u64+i); // 假设这是正确的调用方式
let passcode_six_digits = ensure_six_digits(passcode.to_string());
otps_html.push_str(&format!("<p>i:{}, OTP: {}</p>", i, passcode_six_digits));
}
(StatusCode::OK, Html(format!("<h4>Generated OTPs</h4>{},user_otp:{},key_url:{}", otps_html, params.user_otp.as_ref().unwrap_or(&"".to_string()), key_url))).into_response()
}
fn ensure_six_digits(mut otp: String) -> String {
while otp.len() < 6 {
otp.insert(0, '0');
}
otp
}
#[tokio::main]
async fn main() {
let app = Router::new().route("/generate_otp", get(generate_otp));
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
println!("Listening on {}", addr);
// 绑定地址并启动服务器
axum::Server::bind(&addr).serve(app.into_make_service())
.await
.unwrap();
}