1. 引言
JSON Web Token (JWT) 是一种广泛使用的身份验证机制,但由于其无状态性和自包含性,JWT 中的敏感信息可能会暴露在传输过程中。为了保护这些敏感信息,可以使用加密算法对 JWT 进行加密。本文将深入探讨如何使用 RSA 256 算法(RS256)对 JWT 进行加密和签名处理,以提升身份验证的安全性。
2. JWT 加密的基本概念
JWT 通常使用签名来验证其完整性,但有时需要对 JWT 中的敏感信息进行加密。JWT 加密可以通过在 JWT 头部中指定加密算法来实现。加密后的 JWT 不仅包含签名,还包含加密后的载荷,确保敏感信息在传输过程中不会被窃取或篡改。
3. JWT 加密的工作原理
JWT 加密的工作原理如下:
生成 JWT:服务器生成 JWT,并在头部中指定加密算法。 加密载荷:使用指定的加密算法对 JWT 的载荷进行加密。 签名 JWT:对加密后的 JWT 进行签名,确保其完整性。 传输 JWT:将加密后的 JWT 传输给客户端。 解密 JWT:客户端收到 JWT 后,使用相同的密钥对 JWT 进行解密和验证。
4. 使用 RS256 算法进行加解密以及签名处理
RS256 是一种非对称加密算法,使用 RSA 公钥进行加密和签名,使用 RSA 私钥进行解密和验证。RS256 算法具有以下优点:
安全性高:RSA 算法是一种广泛使用的非对称加密算法,具有较高的安全性。 灵活性:可以使用不同的公钥和私钥对 JWT 进行加密和解密,适用于分布式系统。
5. 实战示例:使用 RS256 算法保护 JWT 中的敏感信息
我们将使用 Rust 和jsonwebtoken
库来实现 JWT 加密,并使用Salvo 0.73
框架来处理 HTTP 请求。
5.1 创建项目
首先,创建一个新的 Rust 项目并添加必要的依赖:
cargo new jwt_encryption_demo
cd jwt_encryption_demo
在Cargo.toml
文件中添加以下依赖:
[dependencies]
salvo = "0.73"
jsonwebtoken = "8.1"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
chrono = "0.4"
rsa = "0.9.6"
base64 = "0.22"
5.2 定义 JWT 载荷
创建一个src/models.rs
文件,定义 JWT 载荷结构:
use serde::{Deserialize, Serialize};
use chrono::{DateTime, Utc};
#[derive(Debug, Serialize, Deserialize)]
pub struct Claims {
pub sub: String, // 用户ID
pub exp: i64, // 过期时间
pub iat: i64, // 签发时间
}
impl Claims {
pub fn new(sub: String, exp: DateTime<Utc>) -> Self {
Self {
sub,
exp: exp.timestamp(),
iat: Utc::now().timestamp(),
}
}
}
5.3 生成 RSA 密钥对
创建一个src/keys.rs
文件,生成 RSA 密钥对:
use rsa::{RsaPrivateKey, RsaPublicKey, PaddingScheme};
use rand::rngs::OsRng;
pub fn generate_rsa_keys() -> (RsaPrivateKey, RsaPublicKey) {
let mut rng = OsRng;
let bits = 2048;
let private_key = RsaPrivateKey::new(&mut rng, bits).expect("failed to generate a key");
let public_key = RsaPublicKey::from(&private_key);
(private_key, public_key)
}
5.4 实现 JWT 生成和验证
创建一个src/jwt.rs
文件,实现 JWT 的生成和验证逻辑:
use jsonwebtoken::{encode, decode, Header, EncodingKey, DecodingKey, Validation, errors::Error, TokenData};
use chrono::{Duration, Utc};
use crate::models::Claims;
use crate::keys::{generate_rsa_keys};
use rsa::{RsaPrivateKey, RsaPublicKey, Pkcs1v15Encrypt};
use rand::rngs::OsRng;
use base64;
use base64::engine::general_purpose::STANDARD;
use base64::Engine as _;
pub fn generate_encrypted_token(user_id: &str) -> Result<String, Error> {
let (private_key, public_key) = generate_rsa_keys();
let claims = Claims::new(user_id.to_string(), Utc::now() + Duration::minutes(15));
let header = Header {
alg: jsonwebtoken::Algorithm::RS256,
..Default::default()
};
let token = encode(&header, &claims, &EncodingKey::from_rsa_pem(private_key.to_pem().as_bytes())?)?;
let encrypted_token = public_key.encrypt(&mut OsRng, Pkcs1v15Encrypt, token.as_bytes())?;
Ok(STANDARD::encode(encrypted_token))
}
pub fn validate_encrypted_token(token: &str) -> Result<TokenData<Claims>, Error> {
let (private_key, public_key) = generate_rsa_keys();
let encrypted_token = STANDARD::decode(token)?;
let decrypted_token = private_key.decrypt(Pkcs1v15Encrypt, &encrypted_token)?;
let token = String::from_utf8(decrypted_token)?;
decode::<Claims>(&token, &DecodingKey::from_rsa_pem(public_key.to_pem().as_bytes())?, &Validation::new(jsonwebtoken::Algorithm::RS256))
}
5.5 实现 HTTP 路由
在src/main.rs
文件中,使用 Salvo 框架实现 HTTP 路由:
mod models;
mod jwt;
mod keys;
use salvo::prelude::*;
use salvo::http::StatusCode;
use serde::Deserialize;
use crate::jwt::{generate_encrypted_token, validate_encrypted_token};
#[derive(Deserialize)]
struct LoginRequest {
username: String,
password: String,
}
#[handler]
async fn login(req: &mut Request, res: &mut Response) {
let login_req: LoginRequest = req.parse_json().await.unwrap();
// 模拟用户验证
if login_req.username == "user1" && login_req.password == "password1" {
let access_token = generate_encrypted_token(&login_req.username).unwrap();
res.render(Json(json!({ "access_token": access_token })));
} else {
res.status_code(StatusCode::UNAUTHORIZED);
res.render(Json(json!({ "message": "Invalid credentials" })));
}
}
#[handler]
async fn profile(req: &mut Request, res: &mut Response) {
if let Some(access_token) = req.headers().get("Authorization") {
let access_token = access_token.to_str().ok().unwrap().split_whitespace().last().unwrap();
match validate_encrypted_token(access_token) {
Ok(claims) => {
res.render(Json(json!({ "user": claims.claims })));
}
Err(_) => {
res.status_code(StatusCode::FORBIDDEN);
res.render(Json(json!({ "message": "Invalid access token" })));
}
}
} else {
res.status_code(StatusCode::UNAUTHORIZED);
res.render(Json(json!({ "message": "No access token provided" })));
}
}
#[tokio::main]
async fn main() {
let router = Router::new()
.push(Router::with_path("login").post(login))
.push(Router::with_path("profile").get(profile));
let acceptor = TcpListener::new("127.0.0.1:3000").bind().await;
Server::new(acceptor).serve(router).await;
}
5.6 运行服务器
在终端中运行以下命令启动服务器:
cargo run
5.7 测试 JWT 加密
登录并获取加密的 JWT:
使用 Postman 或 curl 发送 POST 请求到
/login
接口:
curl -X POST http://localhost:3000/login -H "Content-Type: application/json" -d '{"username": "user1", "password": "password1"}'
响应将包含加密的 JWT:
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyMSIsImV4cCI6MTYxNjIwMDAwMCwiaWF0IjoxNjE2MjAzNjAwfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
}
访问受保护的资源:
使用加密的 JWT 访问
/profile
接口:
curl -X GET http://localhost:3000/profile -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyMSIsImV4cCI6MTYxNjIwMDAwMCwiaWF0IjoxNjE2MjAzNjAwfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
响应将包含用户信息:
{
"user": {
"sub": "user1",
"exp": 1616200000,
"iat": 1616203600
}
}
6. 总结
通过本文,你已经了解了如何使用 RSA 256 算法对 JWT 进行加密和签名处理,以提升身份验证的安全性。通过在 JWT 头部中指定加密算法,并对 JWT 的载荷进行加密,可以确保敏感信息在传输过程中不会被窃取或篡改。希望这些实践能帮助你构建更加安全和可靠的 Web 应用。
7. 进阶阅读
JWT 安全性:了解如何保护 JWT 免受常见攻击,如重放攻击、XSS 和 CSRF。 JWT 刷新机制:学习如何实现 JWT 的刷新机制,以避免用户频繁登录。 JWT 与 OAuth2:探索 JWT 在 OAuth2 身份验证流程中的应用。
通过不断学习和实践,你将能够构建更加安全和高效的身份验证系统。
无论身在何处
有我不再孤单孤单
长按识别二维码关注我们