引言
在手机APP研发中,用户授权是确保应用安全性和用户体验的关键环节。本文将详细介绍如何使用 Rust 语言设计和实现一个用户授权架构,涵盖 JWT 生成、验证、密钥管理、HTTPS 加密传输、短有效期、二次加密、黑名单机制和监控日志等安全规避策略。我们将使用rsa
crate 0.9.6 处理 RSA 密钥,使用salvo
0.74 作为 Web 框架,并使用 PKCS8 格式的证书。
架构设计
1. 用户授权流程
用户登录:用户输入用户名和密码,前端发送登录请求到后端。 JWT 生成:后端验证用户身份后,生成 JWT。 JWT 返回:后端将 JWT 返回给前端。 JWT 存储:前端将 JWT 存储在本地(如 LocalStorage 或 Secure Cookie)。 JWT 验证:前端在每次请求时,将 JWT 附加在请求头中,后端验证 JWT 的签名和有效期。
2. 技术栈
Rust:后端开发语言。 Salvo:Rust 的 Web 框架。 jsonwebtoken:Rust 的 JWT 库。 rsa:Rust 的 RSA 库,用于生成和管理密钥对。 serde:Rust 的序列化和反序列化库。 tokio:Rust 的异步运行时。
实战代码
1. 项目结构
user-auth-app/
├── Cargo.toml
├── src/
│ ├── main.rs
│ ├── auth.rs
│ ├── jwt.rs
│ ├── key_manager.rs
│ ├── blacklist.rs
│ └── logger.rs
└── .env
2. Cargo.toml
[package]
name = "user-auth-app"
version = "0.1.0"
edition = "2021"
[dependencies]
salvo = "0.74"
jsonwebtoken = "9"
rsa = "0.9.6"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
tokio = { version = "1", features = ["full"] }
dotenv = "0.15"
chrono = "0.4"
log = "0.4"
env_logger = "0.9"
3. main.rs
use salvo::prelude::*;
use dotenv::dotenv;
use std::env;
mod auth;
mod jwt;
mod key_manager;
mod blacklist;
mod logger;
#[tokio::main]
async fn main() {
dotenv().ok();
logger::init();
let key_manager = key_manager::KeyManager::new();
let blacklist = blacklist::Blacklist::new();
let router = Router::new()
.push(Router::with_path("login").post(auth::login))
.push(Router::with_path("protected").get(auth::protected));
let server = Server::new(router)
.bind("127.0.0.1:8080")
.await
.unwrap();
server.run().await;
}
4. auth.rs
use salvo::prelude::*;
use serde::{Deserialize, Serialize};
use crate::jwt::{Claims, encode_jwt, decode_jwt};
use crate::key_manager::KeyManager;
use crate::blacklist::Blacklist;
#[derive(Deserialize)]
struct LoginRequest {
username: String,
password: String,
}
#[derive(Serialize)]
struct LoginResponse {
token: String,
}
pub async fn login(
req: &mut Request,
depot: &mut Depot,
res: &mut Response,
) {
let key_manager = depot.obtain::<KeyManager>().unwrap();
let login_req: LoginRequest = req.parse_json().await.unwrap();
// 模拟用户验证
if login_req.username == "user" && login_req.password == "password" {
let claims = Claims {
sub: login_req.username.clone(),
exp: chrono::Utc::now() + chrono::Duration::minutes(15),
};
let token = encode_jwt(&claims, &key_manager.get_private_key()).unwrap();
res.render(Json(LoginResponse { token }));
} else {
res.status_code(StatusCode::UNAUTHORIZED);
}
}
pub async fn protected(
req: &mut Request,
depot: &mut Depot,
res: &mut Response,
) {
let key_manager = depot.obtain::<KeyManager>().unwrap();
let blacklist = depot.obtain::<Blacklist>().unwrap();
if let Some(token) = req.header("Authorization") {
let token = token.replace("Bearer ", "");
if blacklist.contains(&token).await {
res.status_code(StatusCode::UNAUTHORIZED);
return;
}
match decode_jwt(&token, &key_manager.get_public_key()) {
Ok(claims) => res.render(Json(claims)),
Err(_) => res.status_code(StatusCode::UNAUTHORIZED),
}
} else {
res.status_code(StatusCode::UNAUTHORIZED);
}
}
5. jwt.rs
use jsonwebtoken::{encode, decode, Header, EncodingKey, DecodingKey, Validation, errors::Error};
use serde::{Deserialize, Serialize};
use chrono::{DateTime, Utc};
#[derive(Debug, Serialize, Deserialize)]
pub struct Claims {
pub sub: String,
pub exp: DateTime<Utc>,
}
pub fn encode_jwt(claims: &Claims, private_key: &[u8]) -> Result<String, Error> {
encode(&Header::default(), claims, &EncodingKey::from_rsa_pem(private_key)?)
}
pub fn decode_jwt(token: &str, public_key: &[u8]) -> Result<Claims, Error> {
decode::<Claims>(token, &DecodingKey::from_rsa_pem(public_key)?, &Validation::default())
.map(|data| data.claims)
}
6. key_manager.rs
use rsa::{RsaPrivateKey, RsaPublicKey, pkcs8::{EncodePublicKey, LineEnding}};
use std::sync::Arc;
use tokio::sync::RwLock;
#[derive(Clone)]
pub struct KeyManager {
private_key: Arc<RwLock<Vec<u8>>>,
public_key: Arc<RwLock<Vec<u8>>>,
}
impl KeyManager {
pub fn new() -> Self {
let mut rng = rand::thread_rng();
let bits = 2048;
let private_key = RsaPrivateKey::new(&mut rng, bits).expect("failed to generate a key");
let public_key = RsaPublicKey::from(&private_key);
let private_key_pem = private_key.to_pkcs8_pem(LineEnding::LF).unwrap();
let public_key_pem = public_key.to_public_key_pem(LineEnding::LF).unwrap();
Self {
private_key: Arc::new(RwLock::new(private_key_pem.as_bytes().to_vec())),
public_key: Arc::new(RwLock::new(public_key_pem.as_bytes().to_vec())),
}
}
pub async fn get_private_key(&self) -> Vec<u8> {
self.private_key.read().await.clone()
}
pub async fn get_public_key(&self) -> Vec<u8> {
self.public_key.read().await.clone()
}
}
7. blacklist.rs
use std::collections::HashSet;
use std::sync::Arc;
use tokio::sync::RwLock;
#[derive(Clone)]
pub struct Blacklist {
tokens: Arc<RwLock<HashSet<String>>>,
}
impl Blacklist {
pub fn new() -> Self {
Self {
tokens: Arc::new(RwLock::new(HashSet::new())),
}
}
pub async fn add(&self, token: &str) {
self.tokens.write().await.insert(token.to_string());
}
pub async fn contains(&self, token: &str) -> bool {
self.tokens.read().await.contains(token)
}
}
8. logger.rs
use log::{info, error};
use env_logger::Env;
pub fn init() {
env_logger::Builder::from_env(Env::default().default_filter_or("info")).init();
info!("Logger initialized");
}
9. .env
RUST_LOG=info
总结
本文详细介绍了如何在手机APP研发中使用 Rust 语言设计和实现一个用户授权架构。通过使用 Salvo、jsonwebtoken、rsa 等库,实现了 JWT 生成、验证、密钥管理、HTTPS 加密传输、短有效期、二次加密、黑名单机制和监控日志等安全规避策略。实战代码展示了如何构建一个安全可靠的用户授权系统,确保用户数据的安全和隐私。
无论身在何处
有我不再孤单孤单
长按识别二维码关注我们