12个版本 (6个重大更新)
0.7.0 | 2020年5月8日 |
---|---|
0.6.1 | 2020年3月14日 |
0.5.0 | 2020年2月29日 |
0.4.2 | 2020年2月23日 |
0.1.2 | 2020年2月15日 |
#507 in 身份验证
每月25次下载
130KB
2.5K SLoC
待办事项
- 添加更多示例
- 提高覆盖率
功能
- 管理并编排JWT用于用户登录、登出和续订
- 选项1:DynamicVault(使用动态分发,但需要较少的样板代码)
- 选项2:DefaultVault(使用静态分发,但需要较多的样板代码)
- 异步就绪
- 易于开始
- 无不安全代码
- 在稳定版Rust上运行
- 使用Argon(见视频)
- 库方法(不需要运行时)
- 支持可插拔组件
- 在刷新令牌续订时无效化旧刷新令牌
- 在续订新认证令牌时无效化旧认证令牌
- 不允许交叉喂养
- 处理认证令牌过期时的雷霆群问题
- 与任何Web服务器、任何密码散列和任何后端(这里)一起工作
- 带有actix和postgres的完整功能webserver示例
快速入门
先决条件
[dependencies]
jwtvault = "*"
$ curl https://raw.githubusercontent.com/sgrust01/jwtvault/master/generate_certificates.sh > ./generate_certificates.sh
$ chmod 700 generate_certificates.sh && ./generate_certificates.sh
选项1:DynamicVault
use std::collections::HashMap;
use std::collections::hash_map::DefaultHasher;
use jwtvault::prelude::*;
fn main() {
let hasher = ArgonPasswordHasher::default();
// User: John Doe
let user_john = "john_doe";
let password_for_john = "john";
// Save value 'hashed_password_for_john' to persistent storage
// This is more relevant during user signup/password reset
let hashed_password_for_john = hasher.hash_user_password(user_john, password_for_john).unwrap();
// User: Jane Doe
let user_jane = "jane_doe";
let password_for_jane = "jane";
// Save 'hashed_password_for_jane' to persistent storage
// This is more relevant during user signup/password reset
let hashed_password_for_jane = hasher.hash_user_password(user_jane, password_for_jane).unwrap();
let mut users = HashMap::new();
// load users and their password from database/somewhere
users.insert(user_john.to_string(), hashed_password_for_john.to_string());
users.insert(user_jane.to_string(), hashed_password_for_jane.to_string());
// Setup app users
let login = LoginInfo::new(users);
// Initialize vault
let mut vault = DynamicVault::default(Box::new(login));
// John needs to login now
let token = block_on(vault.login(
user_john,
password_for_john,
None,
None,
));
let token = token.ok().unwrap();
// When John presents authentication token, it can be used to restore John's session info
let server_refresh_token = block_on(resolve_session_from_client_authentication_token(
&mut vault,
user_john,
token.authentication(),
));
let server_refresh_token = server_refresh_token.ok().unwrap();
// server_refresh_token (variable) contains server method which captures client private info
// which never leaves the server
let private_info_about_john = server_refresh_token.server().unwrap();
let key = digest::<_, DefaultHasher>(user_john);
let data_on_server_side = private_info_about_john.get(&key).unwrap();
// server_refresh_token (variable) contains client method which captures client public info
// which is also send back to client
assert!(server_refresh_token.client().is_none());
// Check out the data on client and server which are public and private respectively
println!("[Private] John Info: {}",
String::from_utf8_lossy(data_on_server_side.as_slice()).to_string());
// lets renew authentication token
let new_token = block_on(vault.renew(
user_john,
token.refresh(),
None,
));
let new_token = new_token.ok().unwrap();
// When John presents new authentication token it can be used to restore session info
let result = block_on(resolve_session_from_client_authentication_token(
&mut vault,
user_john,
new_token.as_str(),
));
let _ = result.ok().unwrap();
}
选项2:DefaultVault
use jwtvault::prelude::*;
use std::collections::HashMap;
use std::collections::hash_map::DefaultHasher;
fn main() {
let mut users = HashMap::new();
let loader = CertificateManger::default();
// User: John Doe
let user_john = "john_doe";
let password_for_john = "john";
// This should ideally be pre-computed during user sign-up/password reset/change password
let hashed_password_for_john = hash_password_with_argon(
password_for_john,
loader.password_hashing_secret().as_str(),
).unwrap();
// User: Jane Doe
let user_jane = "jane_doe";
let password_for_jane = "jane";
// This should ideally be pre-computed during user sign-up/password reset/change password
let hashed_password_for_jane = hash_password_with_argon(
password_for_jane,
loader.password_hashing_secret().as_str(),
).unwrap();
// load users and their (argon hashed) password from database/somewhere
users.insert(user_john.to_string(), hashed_password_for_john);
users.insert(user_jane.to_string(), hashed_password_for_jane);
// Initialize vault
let mut vault = DefaultVault::new(loader, users, false);
// John needs to login now
let token = block_on(vault.login(
user_john,
password_for_john,
None,
None,
));
let token = token.ok().unwrap();
// When John presents authentication token, it can be used to restore John's session info
let server_refresh_token = block_on(resolve_session_from_client_authentication_token(
&mut vault,
user_john,
token.authentication(),
));
let server_refresh_token = server_refresh_token.ok().unwrap();
// server_refresh_token (variable) contains server method which captures client private info
// which never leaves the server
let private_info_about_john = server_refresh_token.server().unwrap();
let key = digest::<_, DefaultHasher>(user_john);
let data_on_server_side = private_info_about_john.get(&key).unwrap();
// server_refresh_token (variable) contains client method which captures client public info
// which is also send back to client
assert!(server_refresh_token.client().is_none());
// Check out the data on client and server which are public and private respectively
println!("[Private] John Info: {}",
String::from_utf8_lossy(data_on_server_side.as_slice()).to_string());
// lets renew authentication token
let new_token = block_on(vault.renew(
user_john,
token.refresh(),
None,
));
let new_token = new_token.ok().unwrap();
// When John presents new authentication token it can be used to restore session info
let result = block_on(resolve_session_from_client_authentication_token(
&mut vault,
user_john,
new_token.as_str(),
));
let _ = result.ok().unwrap();
}
工作流程
-
开始使用
login
与用户和密码-
成功登录后,将向用户提供JWT对(认证/刷新)
-
认证令牌用于访问任何资源
-
刷新令牌用于在过期时续订认证令牌
-
-
使用
resolve_session_from_client_authentication_token
与 用户 和 认证令牌 来恢复用户会话 -
使用
renew
与 用户 和 刷新令牌 来生成新的认证令牌 -
使用
logout
与 用户 和 认证令牌 将移除与用户关联的所有令牌 -
使用辅助函数
continue_generate_temporary_token
为用户生成临时令牌- 临时令牌创建临时会话,不会破坏原始会话信息
- 临时令牌不能用于登录/登出/续签/撤销原始会话
- 临时令牌没有刷新密钥(这是故意的),以避免令牌刷新
- 注意: DynamicVault 用户可以直接使用实例方法
generate_temporary_token
- 典型用例: 忘记密码
-
使用辅助函数
resolve_temporary_session_from_client_authentication_token
来恢复用户临时会话- 临时会话是隔离的实例,因此没有原始会话的指纹
- 典型用例: 重置密码
依赖项
~15–21MB
~451K SLoC