Axum 是一个基于 Hyper 的 Rust Web 框架,它提供了一种简单、高效的方式来构建 Web 应用。 在实际应用中,我们通常需要对 API 端点进行授权,以确保只有授权用户才能访问受保护的资源。 JWT(JSON Web Token)是一种流行的授权机制,它可以帮助我们实现这一目标。
本文大体分为三部分:
- 生成一个新的密钥对
- 生成和验证Token
- 在Axum中使用授权中间件
1. 生成 Ed25519 的公私钥
在我们的授权系统中,我们使用 Ed25519 算法来生成公私钥对。 Ed25519 是一种现代的、安全的数字签名算法,它提供了高安全性和高性能。
首先,让我们看看生成密钥对的函数:
use anyhow::Result;
use jwt_simple::prelude::*;
use std::fs::File;
fn main() -> Result<()> {
generate_and_save_keys()?;
Ok(())
}
fn generate_and_save_keys() -> Result<()> {
let key_pair = Ed25519KeyPair::generate();
// 保存私钥(只包含私钥信息)
let private_key_pem = key_pair.to_pem();
let mut private_key_file = File::create("private_key.pem")?;
private_key_file.write_all(private_key_pem.as_bytes())?;
// 保存公钥(只包含公钥信息 )
let public_key_pem = key_pair.public_key().to_pem();
let mut public_key_file = File::create("public_key.pem")?;
public_key_file.write_all(public_key_pem.as_bytes())?;
Ok(())
}
这个函数完成了以下几个步骤:
- 使用
Ed25519KeyPair::generate()
生成一个新的密钥对。 - 将私钥转换为 PEM 格式,并保存到 "private_key.pem" 文件中。
- 将公钥转换为 PEM 格式,并保存到 "public_key.pem" 文件中。
要点:
- 密钥对只需要生成一次,然后可以重复使用。
- 私钥必须保密,而公钥可以公开。
- PEM 格式是一种常用的密钥存储格式,易于管理和使用。
tip
把公钥和私钥分开存储,是为了保证私钥的安全性。 私钥只有在生成 token 时才会用到,而公钥则用于验证 token 的签名。 可以将私钥存储在安全的地方,而公钥可以公开发布。 可以将授权系统的公钥存储在一个公开的地方,以便其他服务可以验证 token。
2. 生成 token 和验证 token
接下来,我们来看看如何生成和验证 JWT token。
生成 token
fn sign(user: impl Into<User>) -> Result<String> {
let private_key_pem = read_to_string("private_key.pem")?;
let key_pair = Ed25519KeyPair::from_pem(&private_key_pem)?;
let user = user.into();
let claims = Claims::with_custom_claims(user, Duration::from_secs(JWT_DURATION));
let claims = claims.with_issuer(JWT_ISS).with_audience(JWT_AUD);
let token = key_pair.sign(claims)?;
Ok(token)
}
这个函数完成了以下步骤:
- 从文件中读取私钥。
- 创建一个包含用户信息的 claims 对象。
- 设置 token 的有效期、发行者(
issuer
)和受众(audience
)。 - 使用私钥签名 claims,生成 JWT token。
验证 token
fn verify(token: &str) -> Result<User, Box<dyn std::error::Error>> {
let public_key_pem = read_to_string("public_key.pem")?;
let public_key = Ed25519PublicKey::from_pem(&public_key_pem)?;
let options = VerificationOptions {
allowed_issuers: Some(HashSet::from_strings(&[JWT_ISS])),
allowed_audiences: Some(HashSet::from_strings(&[JWT_AUD])),
..Default::default()
};
let claims = public_key.verify_token::<User>(token, Some(options))?;
Ok(claims.custom)
}
验证函数执行以下步骤:
- 从文件中读取公钥。
- 设置验证选项,包括允许的发行者和受众。
- 使用公钥验证 token 的签名,并解析出 claims。
- 返回自定义的 User 数据。
要点:
- 生成 token 时使用私钥,验证 token 时使用公钥。
- 设置适当的 token 有效期、发行者和受众可以增加安全性。
- 验证时要检查这些元数据以确保 token 的有效性。