JWT 加密:保护敏感信息,提升身份验证的安全性

文摘   2024-10-31 00:31   北京  

1. 引言

JSON Web Token (JWT) 是一种广泛使用的身份验证机制,但由于其无状态性和自包含性,JWT 中的敏感信息可能会暴露在传输过程中。为了保护这些敏感信息,可以使用加密算法对 JWT 进行加密。本文将深入探讨如何使用 RSA 256 算法(RS256)对 JWT 进行加密和签名处理,以提升身份验证的安全性。

2. JWT 加密的基本概念

JWT 通常使用签名来验证其完整性,但有时需要对 JWT 中的敏感信息进行加密。JWT 加密可以通过在 JWT 头部中指定加密算法来实现。加密后的 JWT 不仅包含签名,还包含加密后的载荷,确保敏感信息在传输过程中不会被窃取或篡改。

3. JWT 加密的工作原理

JWT 加密的工作原理如下:

  1. 生成 JWT:服务器生成 JWT,并在头部中指定加密算法。
  2. 加密载荷:使用指定的加密算法对 JWT 的载荷进行加密。
  3. 签名 JWT:对加密后的 JWT 进行签名,确保其完整性。
  4. 传输 JWT:将加密后的 JWT 传输给客户端。
  5. 解密 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 加密

  1. 登录并获取加密的 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"
}
  1. 访问受保护的资源

    使用加密的 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. 进阶阅读

通过不断学习和实践,你将能够构建更加安全和高效的身份验证系统。


无论身在何处

有我不再孤单孤单

长按识别二维码关注我们




育儿之家 YEZJ
“Rust编程之道”,带你探索Rust语言之美,精进编程技艺,开启无限可能!🦀🦀🦀
 最新文章