1 个不稳定版本

0.17.0 2024年1月16日

#1244 in 加密学

每月 下载 39

MIT 许可证

70KB
1.5K SLoC

JWT 构建状态 最新版本 文档

一个 JSON Web 令牌库。

仅声明

如果您不关心该头部,只要头部被验证,就可以使用几个特质来完成签名和验证。

签名

声明可以是任何 serde::Serialize 类型,通常通过 serde_derive 派生。

use hmac::{Hmac, Mac};
use jwt::SignWithKey;
use sha2::Sha256;
use std::collections::BTreeMap;

let key: Hmac<Sha256> = Hmac::new_from_slice(b"some-secret").unwrap();
let mut claims = BTreeMap::new();
claims.insert("sub", "someone");

let token_str = claims.sign_with_key(&key).unwrap();

assert_eq!(token_str, "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJzb21lb25lIn0.5wwE1sBrs-vftww_BGIuTVDeHtc1Jsjo-fiHhDwR8m0");

验证

声明可以是任何 serde::Deserialize 类型,通常通过 serde_derive 派生。

use hmac::{Hmac, Mac};
use jwt::VerifyWithKey;
use sha2::Sha256;
use std::collections::BTreeMap;

let key: Hmac<Sha256> = Hmac::new_from_slice(b"some-secret").unwrap();
let token_str = "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJzb21lb25lIn0.5wwE1sBrs-vftww_BGIuTVDeHtc1Jsjo-fiHhDwR8m0";

let claims: BTreeMap<String, String> = token_str.verify_with_key(&key).unwrap();

assert_eq!(claims["sub"], "someone");

头部和声明

如果您需要自定义头部,可以使用 Token 结构。为了方便,提供了一个 Header 结构,用于所有常用字段,但任何实现了 JoseHeader 的类型都可以使用。

签名

头部和声明都必须实现 serde::Serialize

use hmac::{Hmac, Mac};
use jwt::{AlgorithmType, Header, SignWithKey, Token};
use sha2::Sha384;
use std::collections::BTreeMap;

let key: Hmac<Sha384> = Hmac::new_from_slice(b"some-secret").unwrap();
let header = Header {
    algorithm: AlgorithmType::Hs384,
    ..Default::default()
};
let mut claims = BTreeMap::new();
claims.insert("sub", "someone");

let token = Token::new(header, claims).sign_with_key(&key).unwrap();

assert_eq!(token.as_str(), "eyJhbGciOiJIUzM4NCJ9.eyJzdWIiOiJzb21lb25lIn0.WM_WnPUkHK6zm6Wz7zk1kmIxz990Te7nlDjQ3vzcye29szZ-Sj47rLNSTJNzpQd_");

验证

头部和声明都必须实现 serde::Deserialize

use hmac::{Hmac, Mac};
use jwt::{AlgorithmType, Header, Token, VerifyWithKey};
use sha2::Sha384;
use std::collections::BTreeMap;

let key: Hmac<Sha384> = Hmac::new_from_slice(b"some-secret").unwrap();
let token_str = "eyJhbGciOiJIUzM4NCJ9.eyJzdWIiOiJzb21lb25lIn0.WM_WnPUkHK6zm6Wz7zk1kmIxz990Te7nlDjQ3vzcye29szZ-Sj47rLNSTJNzpQd_";

let token: Token<Header, BTreeMap<String, String>, _> = VerifyWithKey::verify_with_key(token_str, &key).unwrap();
let header = token.header();
let claims = token.claims();

assert_eq!(header.algorithm, AlgorithmType::Hs384);
assert_eq!(claims["sub"], "someone");

存储

可以使用 Store 来表示按键 ID 索引的一组键。目前,这仅自动实现为具有 Borrow<str> 键的 BTreeMapHashMap。如果实现了特殊化,则将实现为所有具有 Index<&str> 特性的类型,就像之前一样。

对于特性和 SignWithStore,关键ID将自动添加到裸声明的头中。因为声明没有指定关键ID的方式,所以需要一个关键ID和声明的元组。对于令牌,使用头部的关键ID来获取密钥。

对于特性和 VerifyWithStore,将使用反序列化头部的关键ID来选择要使用的密钥。

use hmac::{Hmac, Mac};
use jwt::{Header, SignWithStore, Token, VerifyWithStore};
use sha2::Sha512;
use std::collections::BTreeMap;

let mut store: BTreeMap<_, Hmac<Sha512>> = BTreeMap::new();
store.insert("first_key", Hmac::new_from_slice(b"first").unwrap());
store.insert("second_key", Hmac::new_from_slice(b"second").unwrap());

let mut claims = BTreeMap::new();
claims.insert("sub", "someone");

let token_str = ("second_key", claims).sign_with_store(&store).unwrap();

assert_eq!(token_str, "eyJhbGciOiJIUzUxMiIsImtpZCI6InNlY29uZF9rZXkifQ.eyJzdWIiOiJzb21lb25lIn0.9gALQon5Mk8r4BjOZ2SJQlauGmT4WUhpN152x9dfKvkPON1VwEN09Id8vjQ0ABlfLJUTVNP36dsdrpYEZDLUcw");

let verified_token: Token<Header, BTreeMap<String, String>, _> = token_str.verify_with_store(&store).unwrap();
assert_eq!(verified_token.claims()["sub"], "someone");
assert_eq!(verified_token.header().key_id.as_ref().unwrap(), "second_key");

支持的算法

通过 RustCrypto 支持纯Rust HMAC。RSA和ECDSA签名的实现通过OpenSSL支持,默认情况下未启用。必须将OpenSSL类型包裹在 PKeyWithDigest 结构中。

  • HS256
  • HS384
  • HS512
  • RS256
  • RS384
  • RS512
  • ES256
  • ES384
  • ES512

依赖项

~8.5MB
~183K SLoC