#JWT 验证
JWT (JSON Web Token) 是一种开放标准 (RFC 7519),用于在各方之间安全地传输信息。它是一种紧凑的、URL安全的方式,以JSON对象形式表示声明,这些声明通常用于身份验证和信息交换。
JWT由三部分组成:
- 头部(Header) - 指定令牌类型和使用的签名算法
- 负载(Payload) - 包含声明(claims),如用户ID、角色、过期时间等
- 签名(Signature) - 用于验证消息在传输过程中没有被更改
JWT在身份验证流程中的典型使用方式:
- 用户登录后,服务器生成JWT令牌
- 令牌返回给客户端并存储(通常在localStorage或cookie中)
- 后续请求中,客户端在Authorization头部携带此令牌
- 服务器验证令牌的有效性并授权访问
提供对 JWT Auth 验证的中间件.
示例代码
main.rs
Cargo.toml
use jsonwebtoken::{self, EncodingKey};
use salvo::http::{Method, StatusError};
use salvo::jwt_auth::{ConstDecoder, QueryFinder};
use salvo::prelude::*;
use serde::{Deserialize, Serialize};
use time::{Duration, OffsetDateTime};
const SECRET_KEY: &str = "YOUR SECRET_KEY";
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct JwtClaims {
username: String,
exp: i64,
}
#[tokio::main]
async fn main() {
tracing_subscriber::fmt().init();
let auth_handler: JwtAuth<JwtClaims, _> =
JwtAuth::new(ConstDecoder::from_secret(SECRET_KEY.as_bytes()))
.finders(vec![
// Box::new(HeaderFinder::new()),
Box::new(QueryFinder::new("jwt_token")),
// Box::new(CookieFinder::new("jwt_token")),
])
.force_passed(true);
let acceptor = TcpListener::new("0.0.0.0:8698").bind().await;
Server::new(acceptor)
.serve(Router::with_hoop(auth_handler).goal(index))
.await;
}
#[handler]
async fn index(req: &mut Request, depot: &mut Depot, res: &mut Response) -> anyhow::Result<()> {
if req.method() == Method::POST {
let (username, password) = (
req.form::<String>("username").await.unwrap_or_default(),
req.form::<String>("password").await.unwrap_or_default(),
);
if !validate(&username, &password) {
res.render(Text::Html(LOGIN_HTML));
return Ok(());
}
let exp = OffsetDateTime::now_utc() + Duration::days(14);
let claim = JwtClaims {
username,
exp: exp.unix_timestamp(),
};
let token = jsonwebtoken::encode(
&jsonwebtoken::Header::default(),
&claim,
&EncodingKey::from_secret(SECRET_KEY.as_bytes()),
)?;
res.render(Redirect::other(format!("/?jwt_token={token}")));
} else {
match depot.jwt_auth_state() {
JwtAuthState::Authorized => {
let data = depot.jwt_auth_data::<JwtClaims>().unwrap();
res.render(Text::Plain(format!(
"Hi {}, have logged in successfully!",
data.claims.username
)));
}
JwtAuthState::Unauthorized => {
res.render(Text::Html(LOGIN_HTML));
}
JwtAuthState::Forbidden => {
res.render(StatusError::forbidden());
}
}
}
Ok(())
}
fn validate(username: &str, password: &str) -> bool {
username == "root" && password == "pwd"
}
static LOGIN_HTML: &str = r#"<!DOCTYPE html>
<html>
<head>
<title>JWT Auth Demo</title>
</head>
<body>
<h1>JWT Auth</h1>
<form action="/" method="post">
<label for="username"><b>Username</b></label>
<input type="text" placeholder="Enter Username" name="username" required>
<label for="password"><b>Password</b></label>
<input type="password" placeholder="Enter Password" name="password" required>
<button type="submit">Login</button>
</form>
</body>
</html>
"#;
[package]
name = "example-jwt-auth"
version.workspace = true
edition.workspace = true
publish.workspace = true
rust-version.workspace = true
[dependencies]
anyhow = "1"
jsonwebtoken = { workspace = true }
salvo = { workspace = true, features = ["anyhow", "jwt-auth"] }
serde = { workspace = true }
time = { workspace = true }
tokio = { workspace = true, features = ["macros"] }
tracing = { workspace = true }
tracing-subscriber = { workspace = true }