Rust API密钥管理库prefixed-api-key的使用,实现安全可识别的带前缀API密钥生成与验证
Rust API密钥管理库prefixed-api-key的使用,实现安全可识别的带前缀API密钥生成与验证
Prefixed API Key (Seam风格)
示例密钥: mycompany_BRTRKFsL_51FwqftsmMDHHbJAMEXXHCgG
Seam风格的API密钥有许多优点:
- 双击API密钥会选择整个API密钥
- 字母表标准化,感谢base58 RFC及其在加密货币中的使用
- 比hex和base32 API密钥更短
- 有前缀,允许GitHub进行秘密扫描
- 有哈希组件,服务器不需要存储API密钥(减少攻击面)
- 有未哈希的短令牌,服务器和密钥持有者/客户可以共同使用来识别API密钥
- 默认与UUIDv4大致相同的熵位数
格式
Seam风格的API密钥看起来像这样:
mycompany_BRTRKFsL_51FwqftsmMDHHbJAMEXXHCgG
分解API密钥的组件:
mycompany ..._... BRTRKFsL ..._... 51FwqftsmMDHHbJAMEXXHCgG
^ ^ ^
前缀 短令牌 长令牌
- 前缀用于识别创建API密钥的公司或服务
- 短令牌由服务器和密钥持有者/客户存储,可用于识别API密钥
- 长令牌用于验证密钥,服务器只存储其哈希值
完整示例代码
use prefixed_api_key::PrefixedApiKeyController;
use sha2::Sha256;
use std::collections::HashMap;
// 模拟数据库存储
struct ApiKeyDatabase {
// short_token -> hash 的映射
keys: HashMap<String, String>,
}
impl ApiKeyDatabase {
fn new() -> Self {
Self {
keys: HashMap::new(),
}
}
fn store_key(&mut self, short_token: String, hash: String) {
self.keys.insert(short_token, hash);
}
fn get_hash_by_short_token(&self, short_token: &str) -> Option<&String> {
self.keys.get(short_token)
}
}
fn main() {
// 1. 创建API密钥控制器
let controller = PrefixedApiKeyController::<_, Sha256>::configure()
.prefix("myapp".to_owned()) // 设置API密钥前缀
.rng_osrng() // 使用操作系统随机数生成器
.short_token_length(8) // 短令牌长度
.long_token_length(24) // 长令牌长度
.finalize()
.expect("Failed to create API key controller");
// 2. 创建模拟数据库
let mut db = ApiKeyDatabase::new();
// 3. 生成并存储API密钥
let (api_key, hash) = controller.try_generate_key_and_hash()
.expect("Failed to generate API key");
db.store_key(api_key.short_token().to_string(), hash);
// 4. 将API密钥发送给客户端
let api_key_str = api_key.to_string();
println!("Generated API key for client: {}", api_key_str);
// 模拟客户端请求 - 使用API密钥访问API
let client_api_key_str = api_key_str.clone();
// 5. 服务器端验证
// 解析API密钥
let client_api_key = client_api_key_str.parse::<prefixed_api_key::PrefixedApiKey>()
.expect("Failed to parse API key string");
// 从数据库获取存储的hash
let stored_hash = db.get_hash_by_short_token(client_api_key.short_token())
.expect("API key not found in database");
// 验证密钥
let is_valid = controller.check_hash(&client_api_key, stored_hash);
println!("API key validation result: {}", is_valid);
// 6. 展示API密钥信息
println!("\nAPI Key Details:");
println!("Prefix: {}", client_api_key.prefix());
println!("Short Token: {}", client_api_key.short_token());
println!("Long Token: {}", client_api_key.long_token());
println!("Full Key: {}", client_api_key);
}
测试说明
库测试命令:
cargo test --all-features
代码质量检查:
cargo fmt --all -- --check
cargo clippy -- -D warnings
1 回复
Prefixed API Key: Rust中安全可识别的API密钥管理库
prefixed_api_key
是一个Rust库,用于生成和验证带有前缀的安全API密钥。这种密钥格式既方便识别又保证了安全性,是现代API开发的理想选择。
主要特性
- 生成带有可读前缀的API密钥
- 使用加密安全的随机数生成器
- 密钥格式:
prefix_xxxxxxxxxxxxxxxx_secret_xxxxxxxxxxxxxxxx
- 支持密钥验证
- 可自定义密钥长度和前缀
安装
在Cargo.toml中添加依赖:
[dependencies]
prefixed_api_key = "0.3"
基本用法
生成新API密钥
use prefixed_api_key::PrefixedApiKey;
fn main() {
// 生成一个新密钥
let pak = PrefixedApiKey::generate("myapp");
println!("API Key: {}", pak.to_string());
println!("Key ID: {}", pak.key_id());
println!("Secret: {}", pak.secret());
}
示例输出:
API Key: myapp_5Hj6u8P3Q2R9s4T7_secret_2w4z6x8y1a3b5c7d
Key ID: 5Hj6u8P3Q2R9s4T7
Secret: 2w4z6x8y1a3b5c7d
验证API密钥
use prefixed_api_key::PrefixedApiKey;
fn main() {
let api_key = "myapp_5Hj6u8P3Q2R9s4T7_secret_2w4z6x8y1a3b5c7d";
match PrefixedApiKey::parse(api_key) {
Ok(pak) => {
println!("Valid API key for prefix: {}", pak.prefix());
println!("Key ID: {}", pak.key_id());
println!("Secret: {}", pak.secret());
}
Err(e) => println!("Invalid API key: {}", e),
}
}
高级用法
自定义密钥长度
use prefixed_api_key::{PrefixedApiKey, KeyConfig};
fn main() {
let config = KeyConfig {
prefix: "cust",
short_key_length: 10,
long_key_length: 20,
};
let pak = PrefixedApiKey::generate_with_config(config);
println!("Custom API Key: {}", pak);
}
从组件构建密钥
use prefixed_api_key::PrefixedApiKey;
fn main() {
let pak = PrefixedApiKey::from_components(
"myapp",
"5Hj6u8P3Q2R9s4T7",
"2w4z6x8y1a3b5c7d",
);
println!("Reconstructed API Key: {}", pak);
}
实际应用示例
在Web服务中使用
use prefixed_api_key::PrefixedApiKey;
use actix_web::{get, App, HttpServer, HttpResponse, web};
#[get("/api/key")]
async fn generate_key() -> HttpResponse {
let pak = PrefixedApiKey::generate("myapp");
HttpResponse::Ok().body(pak.to_string())
}
#[get("/api/validate/{key}")]
async fn validate_key(key: web::Path<String>) -> HttpResponse {
match PrefixedApiKey::parse(&key) {
Ok(pak) => HttpResponse::Ok().json(serde_json::json!({
"valid": true,
"prefix": pak.prefix(),
"key_id": pak.key_id()
})),
Err(_) => HttpResponse::BadRequest().json(serde_json::json!({
"valid": false,
"error": "Invalid API key format"
})),
}
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
App::new()
.service(generate_key)
.service(validate_key)
})
.bind("127.0.0.1:8080")?
.run()
.await
}
安全建议
- 始终通过HTTPS传输API密钥
- 不要将生成的密钥记录在日志中
- 考虑为不同环境使用不同前缀(如
dev_
,prod_
) - 定期轮换密钥
prefixed_api_key
库提供了一种既安全又便于管理的API密钥解决方案,特别适合需要区分不同客户端或服务的应用场景。
完整示例代码
以下是一个完整的Rust项目示例,展示了如何使用prefixed_api_key
库:
// Cargo.toml
/*
[dependencies]
prefixed_api_key = "0.3"
actix-web = "4.0"
serde_json = "1.0"
*/
// src/main.rs
use prefixed_api_key::{PrefixedApiKey, KeyConfig};
use actix_web::{get, post, web, App, HttpResponse, HttpServer, Responder};
use serde_json::json;
// 生成API密钥的端点
#[get("/generate")]
async fn generate() -> impl Responder {
let pak = PrefixedApiKey::generate("prod"); // 生产环境使用prod前缀
HttpResponse::Ok().json(json!({
"api_key": pak.to_string(),
"key_id": pak.key_id(),
"prefix": pak.prefix()
}))
}
// 验证API密钥的端点
#[post("/validate")]
async fn validate(req_body: String) -> impl Responder {
match PrefixedApiKey::parse(&req_body) {
Ok(pak) => HttpResponse::Ok().json(json!({
"valid": true,
"prefix": pak.prefix(),
"key_id": pak.key_id(),
"environment": if pak.prefix() == "dev" {
"development"
} else {
"production"
}
})),
Err(e) => HttpResponse::BadRequest().json(json!({
"valid": false,
"error": e.to_string()
})),
}
}
// 自定义密钥生成的端点
#[get("/custom")]
async fn custom() -> impl Responder {
let config = KeyConfig {
prefix: "test",
short_key_length: 12,
long_key_length: 24,
};
let pak = PrefixedApiKey::generate_with_config(config);
HttpResponse::Ok().body(pak.to_string())
}
// 主函数
#[actix_web::main]
async fn main() -> std::io::Result<()> {
println!("Starting API key server at http://localhost:8080");
HttpServer::new(|| {
App::new()
.service(generate)
.service(validate)
.service(custom)
})
.bind("127.0.0.1:8080")?
.run()
.await
}
这个示例创建了一个完整的Web服务,提供三个端点:
/generate
- 生成新的API密钥/validate
- 验证API密钥的有效性/custom
- 生成自定义长度的API密钥
要运行这个示例:
- 创建新的Rust项目:
cargo new api_key_demo
- 添加上述依赖到Cargo.toml
- 将代码复制到src/main.rs
- 运行:
cargo run
测试方法:
- 访问
http://localhost:8080/generate
获取新密钥 - 使用POST请求
http://localhost:8080/validate
并发送密钥进行验证 - 访问
http://localhost:8080/custom
获取自定义长度的密钥