引言
在现代移动应用中,用户登录和API接口的授权处理是至关重要的安全环节。本文将详细介绍如何使用Rust和Salvo框架构建一个安全的移动应用登录及API接口授权系统。我们将涵盖前端和后端的处理流程,并提供详细的示例代码和文字说明。
1. 系统架构概述
本方案设计一个基于JWT(JSON Web Token)的鉴权系统,确保用户登录信息长期有效,并提供安全的接口交互。
技术栈
前端: 移动应用(iOS/Android) 后端: Rust + Salvo 0.74.1 数据库: PostgreSQL/MySQL 缓存: Redis 加密: JWT (JSON Web Token)
2. 用户登录流程
用户登录: 用户在移动应用中输入用户名和密码。 前端请求: 前端将用户名和密码通过HTTPS发送到后端。 后端验证: 后端验证用户名和密码,生成JWT并返回给前端。 前端存储: 前端存储JWT,并在每次请求时将其附加到请求头中。
3. Cargo.toml 内容
在Cargo.toml
文件中,我们需要添加必要的依赖项,以便使用 Salvo 框架和 JWT 库。
[package]
name = "your_project_name"
version = "0.1.0"
edition = "2021"
[dependencies]
salvo = "0.74.1"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
jsonwebtoken = "9.3"
tokio = { version = "1", features = ["full"] }
chrono = "0.4"
[dependencies.sqlx]
version = "0.5"
features = ["postgres", "runtime-tokio-native-tls"]
[dependencies.redis]
version = "0.21"
[dependencies.dotenv]
version = "0.15"
4. 后端接口设计
4.1 登录接口
首先,我们需要定义一个登录接口,用于验证用户名和密码,并生成JWT。
use salvo::prelude::*;
use salvo::http::StatusCode;
use serde::{Deserialize, Serialize};
use jsonwebtoken::{encode, Header, EncodingKey, decode, DecodingKey, Validation, errors::Error};
use std::time::{SystemTime, UNIX_EPOCH, Duration};
#[derive(Debug, Serialize, Deserialize)]
struct Claims {
sub: String,
exp: u64,
}
#[handler]
async fn login(req: &mut Request, res: &mut Response) {
let username = req.form_value("username").unwrap();
let password = req.form_value("password").unwrap();
// 验证用户名和密码
if let Some(user) = db::find_user_by_username_and_password(username, password).await {
// 生成 JWT
let token = generate_jwt(&user);
res.render(Json(json!({ "token": token })));
} else {
res.status_code(StatusCode::UNAUTHORIZED);
}
}
fn generate_jwt(user: &User) -> String {
let claims = Claims {
sub: user.id.to_string(),
exp: (SystemTime::now() + Duration::from_secs(3600)).duration_since(UNIX_EPOCH).unwrap().as_secs(),
};
encode(&Header::default(), &claims, &EncodingKey::from_secret("secret".as_ref())).unwrap()
}
4.2 验证 JWT
在每个需要授权的接口中,我们需要验证JWT的有效性。
#[handler]
async fn protected_route(req: &mut Request, res: &mut Response) {
let token = req.header("Authorization").unwrap().split_whitespace().nth(1).unwrap();
let claims = decode::<Claims>(token, &DecodingKey::from_secret("secret".as_ref()), &Validation::default()).unwrap().claims;
// 验证通过,继续处理请求
// ...
}
5. 前端处理
5.1 存储 JWT
前端可以使用SecureStore
(iOS)或SharedPreferences
(Android)来安全地存储JWT。
// iOS (Swift)
UserDefaults.standard.set(token, forKey: "authToken")
// Android (Kotlin)
val sharedPreferences = getSharedPreferences("app_prefs", Context.MODE_PRIVATE)
sharedPreferences.edit().putString("authToken", token).apply()
5.2 发送 JWT
每次请求时,前端需要将JWT附加到请求头中。
// iOS (Swift)
let headers = ["Authorization": "Bearer \(token)"]
let request = URLRequest(url: url)
request.allHTTPHeaderFields = headers
// Android (Kotlin)
val request = Request.Builder()
.url(url)
.header("Authorization", "Bearer $token")
.build()
6. 长期有效登录
6.1 刷新 JWT
为了保证登录信息长期有效,可以设计一个刷新机制:
JWT 有效期: 设置较短的有效期(例如 1 小时)。 刷新接口: 提供一个刷新接口,前端在JWT即将过期时调用该接口获取新的JWT。
#[handler]
async fn refresh_token(req: &mut Request, res: &mut Response) {
let token = req.header("Authorization").unwrap().split_whitespace().nth(1).unwrap();
let claims = decode::<Claims>(token, &DecodingKey::from_secret("secret".as_ref()), &Validation::default()).unwrap().claims;
// 验证通过,生成新的 JWT
let new_token = generate_jwt(&db::find_user_by_id(claims.sub).await.unwrap());
res.render(Json(json!({ "token": new_token })));
}
6.2 前端自动刷新
前端可以在JWT即将过期时自动调用刷新接口,确保用户不会因为JWT过期而被迫重新登录。
// iOS (Swift)
func refreshTokenIfNeeded() {
if let token = UserDefaults.standard.string(forKey: "authToken"), let claims = decodeJWT(token) {
let expirationTime = claims.exp
if expirationTime - Date().timeIntervalSince1970 < 60 { // 提前 60 秒刷新
refreshToken()
}
}
}
func refreshToken() {
// 调用刷新接口
}
7. 安全性考虑
HTTPS: 所有接口必须通过HTTPS传输。 JWT 加密: 使用强加密算法(如 HS256)加密JWT。 防止重放攻击: 在JWT中包含时间戳或nonce,并在后端验证。 限流: 对登录和刷新接口进行限流,防止暴力破解。
8. 总结
通过使用JWT和自动刷新机制,我们确保了用户登录信息长期有效。前端负责存储和发送JWT,后端负责验证和生成JWT。通过合理的接口设计和安全性考虑,我们构建了一个安全且高效的移动应用登录及API接口授权系统。
希望这篇教程能够帮助你构建一个优雅且安全的移动应用登录及API接口授权系统。
无论身在何处
有我不再孤单孤单
长按识别二维码关注我们