JWT(JSON Web Token)由三部分组成:Header(头部)、Payload(负载) 和 Signature`(签名),三者通过点号 . 连接。
Header:包含令牌类型(typ: JWT)和签名算法(如alg: HS256)。Payload:携带声明信息,包括标准字段(如 sub 用户主题、exp 过期时间、iat 签发时间)和自定义数据(如用户角色、权限等)。 Signature:使用密钥对base64UrlEncode(header) + "." + base64UrlEncode(payload)进行签名,确保数据完整性。
客户端与服务器端交互流程
Cargo.toml
[dependencies] # 序列化/反序列化 serde = { version = "1.0", features = ["derive"] } # JWT 处理库 jsonwebtoken = { version = "10.3", features = ["rust_crypto"] } # 环境变量管理 dotenv = "0.15" # 日期时间处理 chrono = "0.4" # 随机数生成 rand = "0.10" # Base64 编码解码 base64 = "0.22"导入库
usebase64::engine::general_purpose;usebase64::engine::general_purpose::STANDARD;usebase64::prelude::BASE64_STANDARD;usebase64::Engineas_;usechrono::{Duration,Utc};usedotenv::dotenv;usejsonwebtoken::{decode,encode,Algorithm,DecodingKey,EncodingKey,Header,TokenData,Validation,};useserde::{Deserialize,Serialize};usestd::collections::HashSet;usestd::env;usestd::fs;usestd::io::Write;JWT载荷结构体
/// 定义JWT载荷结构体#[derive(Debug, Serialize, Deserialize)]pubstructClaims{pubsub:String,// 用户标识(subject)pubexp:i64,// 过期时间(expiration)pubiat:i64,// 签发时间pubiss:String,// 签发者(issuer)pubroles:Vec<String>,// 角色权限}implClaims{pubfnnew(sub:String,iss:String,roles:Vec<String>)->Self{letnow=Utc::now().timestamp();letexp=now+3600;// 单位秒。1小时后过期,或 + Duration::hours(1);Self{sub,exp,iat:now,iss,roles,}}}生成密钥
/// 生成随机密钥并保存到文件pubfngenerate_and_save_key()->Result<(),jsonwebtoken::errors::Error>{// 生成32字节的随机密钥(用于HS256算法)letkey=(0..32).map(|_|rand::random::<u8>()).collect::<Vec<u8>>();// 将密钥保存为base64编码的字符串letkey_base64=STANDARD.encode(&key);// 写入到文件letmutfile=std::fs::File::create("jwt_secret.key").unwrap();writeln!(file,"{}",key_base64).unwrap();println!("密钥已生成并保存到 jwt_secret.key 文件");Ok(())}读取密钥
/// 从文件读取密钥pubfnload_secret_key()->Result<Vec<u8>,jsonwebtoken::errors::Error>{// 加载环境变量dotenv().ok();// 优先从环境变量读取密钥ifletOk(secret_env)=env::var("JWT_SECRET"){returnOk(STANDARD.decode(&secret_env)?);}// 如果环境变量不存在,从文件读取letkey_content=fs::read_to_string("jwt_secret.key").unwrap();letkey_base64=key_content.trim();letkey=STANDARD.decode(key_base64)?;Ok(key)}生成 JWT 令牌
/// 生成JWT令牌pubfngenerate_token(claims:&Claims)->Result<String,jsonwebtoken::errors::Error>{// 加载密钥letsecret=load_secret_key()?;// 创建JWT头部letheader=Header::default();// 使用HS256算法和密钥生成JWT令牌// JWT头部 + JWT载荷 + 加密密钥lettoken=encode(&header,&claims,&EncodingKey::from_secret(&secret))?;Ok(token)}验证 JWT
/// 验证JWT令牌pubfnvalidate_token(token:&str)->Result<TokenData<Claims>,jsonwebtoken::errors::Error>{// 加载密钥letsecret=load_secret_key()?;// 配置验证参数letmutvalidation=Validation::new(Algorithm::HS256);validation.leeway=60;// 允许时钟偏差,秒validation.validate_exp=true;// 是否验证 "过期时间" 字段。 如果exp字段中的时间已过,它将返回一个错误。validation.validate_nbf=false;// 是否验证 "生效时间" 字段。早于生效时间报错validation.iss=Some(HashSet::from(["xtsz".to_string()]));// 签发者(issuer)// 解码并验证JWT令牌// JWT令牌 + 解密密钥 + 验证参数lettoken_data=decode::<Claims>(token,&DecodingKey::from_secret(&secret),&validation)?;测试
#[cfg(test)]// 表示以下模块仅在测试模式下编译modtests{// 定义测试模块usesuper::*;usejsonwebtoken::errors::ErrorKind;// 导入父模块中的所有内容#[test]fntest_1()->Result<(),jsonwebtoken::errors::Error>{// 1. 生成并保存密钥(仅首次运行时需要)generate_and_save_key()?;// 2. 生成JWT令牌letclaims=Claims::new("user123".to_string(),// 用户标识"xtsz".to_string(),// 签发者标识vec!["s".to_string()],// 用户权限);lettoken=generate_token(&claims)?;// 生成JWT令牌println!("生成的令牌: {}",token);// 3. 验证JWT令牌lettoken_result=validate_token(&token);// let token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1c2VyMTIzIiwiZXhwIjoxNzc2NzYzMzQwLCJpYXQiOjE3NzY3NjMzMzUsImlzcyI6Inh0c3oiLCJyb2xlcyI6WyJzIl19.oh3xI_u_op-WkdiV77jQ5JF0p0HUqY0_sxhaZXI3eio";// let token_result = validate_token(token);// 获取验证信息matchtoken_result{// 令牌正确Ok(token_data)=>{println!("令牌验证成功,用户: {}",token_data.claims.sub);println!("服务器: {}",token_data.claims.iss);lettime1=Utc::now().timestamp();lettime2=token_data.claims.exp;lettime3=time2-time1;println!("当前时间:{}",time1);println!("过期时间: {}",time2);println!("距离过期还有: {}秒",time3);println!("签发时间:{}",token_data.claims.iat);println!("角色权限:{}",token_data.claims.roles.get(0).unwrap());}// 令牌错误Err(err)=>{matcherr.kind(){ErrorKind::ExpiredSignature=>{eprintln!("Token 过期");}ErrorKind::InvalidIssuer=>{eprintln!("发行方无效");}ErrorKind::InvalidSignature=>{eprintln!("无效签名");}_=>{eprintln!("其他错误: {:?}",err);}}}}Ok(())}}