7个版本 (破坏性)
新 0.6.1 | 2024年8月13日 |
---|---|
0.5.0 | 2024年1月26日 |
0.4.3 |
|
0.4.1 | 2021年2月12日 |
0.1.0 | 2021年2月10日 |
#316 in 身份验证
229 每月下载
用于 4 个crate (3 直接)
25KB
491 行
提供了一个 Actor
和可序列化 Token
结构,支持使用自定义有效载荷验证JSON Web Tokens。有关JWT规范更多信息,请参阅 jwt.io。
提供的 Actor
使用 ECDSA 算法签名令牌(使用 ed25519_dalek
crate)。
与其他JWT实现不同,该库允许递归的 Token
。
请注意,如果在令牌链中指定相同的 (host, actor)
对多次,只有最新的一次由 Claims::get
返回。
示例
use rjwt::*;
#[derive(Clone)]
struct Resolver {
hostname: String,
actors: HashMap<String, Actor<String>>,
peers: Vec<Self>,
}
// ...
#[async_trait]
impl Resolve for Resolver {
type HostId = String;
type ActorId = String;
type Claims = String;
async fn resolve(&self, host: &Self::HostId, actor_id: &Self::ActorId) -> Result<Actor<Self::ActorId>, Error> {
if host == &self.hostname {
self.actors.get(actor_id).cloned().ok_or_else(|| Error::fetch(actor_id))
} else if let Some(peer) = self.peers.iter().filter(|p| &p.hostname == host).next() {
peer.resolve(host, actor_id).await
} else {
Err(Error::fetch(host))
}
}
}
let now = SystemTime::now();
// Say that Bob is a user on example.com.
let bobs_id = "bob".to_string();
let example_dot_com = "example.com".to_string();
let actor_bob = Actor::new(bobs_id.clone());
let example = Resolver::new(example_dot_com.clone(), [actor_bob.clone()], vec![]);
// Bob makes a request through the retailer.com app.
let retailer_dot_com = "retailer.com".to_string();
let retail_app = Actor::new("app".to_string());
let retailer = Resolver::new(
retailer_dot_com.clone(),
[retail_app.clone()],
vec![example.clone()]);
// The retailer.com app makes a request to Bob's bank.
let bank_account = Actor::new("bank".to_string());
let bank = Resolver::new(
"bank.com".to_string(),
[bank_account.clone()],
vec![example, retailer.clone()]);
// First, example.com issues a token to authenticate Bob.
let bobs_claim = String::from("I am Bob and retailer.com may debit my bank.com account");
// This requires constructing the token...
let bobs_token = Token::new(
example_dot_com.clone(),
now,
Duration::from_secs(30),
actor_bob.id().to_string(),
bobs_claim.clone());
// and signing it with Bob's private key.
let bobs_token = actor_bob.sign_token(bobs_token).expect("signed token");
// Then, retailer.com validates the token...
let bobs_token = block_on(retailer.verify(bobs_token.into_jwt(), now)).expect("claims");
assert!(bobs_token.claims().get(&example_dot_com, &bobs_id).expect("claim").starts_with("I am Bob"));
// and adds its own claim, that Bob owes it $1.
let retailer_claim = String::from("Bob spent $1 on retailer.com");
let retailer_token = retail_app.consume_and_sign(
bobs_token,
retailer_dot_com.clone(),
retailer_claim.clone(),
now).expect("signed token");
assert_eq!(retailer_token
.claims()
.get(&retailer_dot_com, retail_app.id()), Some(&retailer_claim));
assert_eq!(retailer_token
.claims()
.get(&example_dot_com, actor_bob.id()), Some(&bobs_claim));
// Finally, Bob's bank verifies the token...
let retailer_token_as_received = block_on(
bank.verify(retailer_token.jwt().to_string(), now)
).expect("claims");
assert_eq!(retailer_token, retailer_token_as_received);
// to authenticate that the request came from Bob...
assert!(retailer_token_as_received
.claims()
.get(&example_dot_com, &bobs_id)
.expect("claim")
.starts_with("I am Bob and retailer.com may debit my bank.com account"));
// via retailer.com.
assert!(retailer_token_as_received
.claims()
.get(&retailer_dot_com, retail_app.id())
.expect("claim")
.starts_with("Bob spent $1"));
依赖项
~3.5–5.5MB
~110K SLoC