Rust身份认证与授权库keycloak的使用:集成Keycloak实现安全的单点登录和访问控制
Rust身份认证与授权库keycloak的使用:集成Keycloak实现安全的单点登录和访问控制
法律声明
本库采用双重许可协议:MIT
或 UNLICENSE
。
特性
实现了Keycloak Admin REST API version 26.3.1。
特性标志
默认标志:tags-all
。
rc
: 使用Arc
进行反序列化schemars
: 添加 schemars 支持multipart
: 为 reqwest 添加 multipart 支持,启用 API 中的额外方法tags-all
: 激活 REST API 中的所有标签(资源组),这是默认行为。禁用默认特性并使用单独的tag-xxx
特性来仅激活所需的资源组。
使用
要求 Rust 版本 >= 1.84.0
。
在 Cargo.toml 中添加依赖:
[dependencies]
keycloak = "~26.3"
以下是完整的示例代码,展示了如何使用 keycloak 库进行身份认证和授权管理:
use keycloak::{
types::*,
{KeycloakAdmin, KeycloakAdminToken},
};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// 获取 Keycloak 服务器地址,默认为本地开发环境
let url = std::env::var("KEYCLOAK_ADDR").unwrap_or_else(|_| "http://localhost:8080".into());
// 获取管理员用户名,默认为 "admin"
let user = std::env::var("KEYCLOAK_USER").unwrap_or_else(|_| "admin".into());
// 获取管理员密码,默认为 "password"
let password = std::env::var("KEYCLOAK_PASSWORD").unwrap_or_else(|_| "password".into());
// 创建 HTTP 客户端
let client = reqwest::Client::new();
// 获取管理员令牌
let admin_token = KeycloakAdminToken::acquire(&url, &user, &password, &client).await?;
eprintln!("{:?}", admin_token);
// 创建 Keycloak 管理员客户端
let admin = KeycloakAdmin::new(&url, admin_token, client);
// 创建一个新的领域(realm)
admin
.post(RealmRepresentation {
realm: Some("test".into()),
..Default::default()
})
.await?;
// 在新领域中创建一个用户
let response = admin
.realm_users_post(
"test",
UserRepresentation {
username: Some("user".into()),
..Default::default()
},
)
.await?;
eprintln!("{:?}", response.to_id());
// 获取领域中的所有用户
let users = admin
.realm_users_get(
"test", None, None, None, None, None, None, None, None, None, None, None, None, None,
None,
)
.await?;
eprintln!("{:?}", users);
// 查找并删除特定用户
let id = users
.iter()
.find(|u| u.username == Some("user".into()))
.unwrap()
.id
.as_ref()
.unwrap()
.to_string();
admin
.realm_users_with_user_id_delete("test", id.as_str())
.await?;
// 删除测试领域
admin.realm_delete("test").await?;
Ok(())
}
版本匹配
如果 keycloak 的版本为 x.y.z
,则本包的版本为 x.y.(z * 100 + v)
,其中 v 是官方 x.y.z
版本的次要修复版本号。
例如:官方版本 13.0.1
对应 crate 版本为 13.0.100
。13.0.102
表示 keycloak 版本为 13.0.1
且次要修复版本为 2
。
更新
要更新当前版本,请使用提供的 update.ts deno 脚本:
deno run --allow-env=KEYCLOAK_RUST_VERSION,KEYCLOAK_VERSION,KEYCLOAK_RUST_MAJOR_VERSION --allow-read=Cargo.toml --allow-write=Cargo.toml,api/openapi.json,src/types.rs,src/rest/generated_rest.rs --allow-net=keycloak.org,www.keycloak.org --allow-run=cargo,gh,git,handlebars-magic update.ts
1 回复
Rust身份认证与授权库Keycloak的使用指南
Keycloak简介
Keycloak是一个开源的身份和访问管理解决方案,提供单点登录(SSO)、OAuth2.0、OpenID Connect和SAML等功能。在Rust生态中,我们可以使用keycloak
crate来集成Keycloak服务。
安装依赖
在Cargo.toml
中添加以下依赖:
[dependencies]
keycloak = "0.13.0"
tokio = { version = "1.0", features = ["full"] }
reqwest = "0.11"
serde = { version = "1.0", features = ["derive"] }
基本使用方法
1. 初始化Keycloak客户端
use keycloak::{KeycloakAdmin, KeycloakAdminToken};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let server_url = "http://localhost:8080/auth";
let username = "admin";
let password = "admin";
let realm = "master";
let client_id = "admin-cli";
let token = KeycloakAdminToken::acquire(
server_url,
username,
password,
realm,
client_id
).await?;
let admin = KeycloakAdmin::new(
server_url,
token,
reqwest::Client::new()
);
Ok(())
}
2. 用户认证流程
use keycloak::{Keycloak, KeycloakError, types::*};
async fn authenticate_user() -> Result<(), KeycloakError> {
let keycloak = Keycloak::new(
"http://localhost:8080/auth",
reqwest::Client::new()
);
let token = keycloak
.realm("myrealm")
.client("myclient")
.direct_access_grant()
.authenticate("username", "password")
.await?;
println!("Access Token: {}", token.access_token);
println!("Refresh Token: {:?}", token.refresh_token);
Ok(())
}
3. 实现单点登录(SSO)
use keycloak::types::OAuthClient;
async fn configure_sso() -> Result<(), KeycloakError> {
let admin = get_admin_client().await?;
// 创建或更新客户端配置
let client = OAuthClient {
client_id: "myapp".to_string(),
name: Some("My Application".to_string()),
redirect_uris: Some(vec![
"http://localhost极客时间
"http://localhost:3000/callback".to_string()
]),
standard_flow_enabled: Some(true),
..Default::default()
};
admin
.realm_clients_post("myrealm", client)
.await?;
Ok(())
}
访问控制示例
1. 检查用户角色
async fn check_user_role(access_token: &str) -> Result<bool, KeycloakError> {
let keycloak = Keycloak::new(
"http://localhost:8080/auth",
reqwest::Client::new()
);
let introspect = keycloak
.realm("myrealm")
.introspect(access_token)
.await?;
if let Some(roles)极客时间 introspect.realm_access.as_ref().and_then(|a| a.roles.as_ref()) {
if roles.contains(&"admin".to_string()) {
return Ok(true);
}
}
Ok(false)
}
2. 资源服务器权限检查
use keycloak::types::PermissionTicket;
async fn check_resource_permission(
access_token: &str,
resource_name: &str,
scope: &str
) -> Result<bool, KeycloakError> {
let keycloak = Keycloak::new(
"http://localhost:8080/auth",
reqwest::Client::new()
);
let permission = PermissionTicket {
resource: resource_name.to_string(),
scope: Some(scope.to_string()),
..Default::default()
};
let decision = keycloak
.realm("myrealm")
.evaluate_permissions(vec![permission], access_token)
.await?;
Ok(decision[0].result)
}
高级功能
1. 自定义协议映射器
use keycloak::types::ProtocolMapperRepresentation;
async fn add_custom_claim() -> Result<(), KeycloakError> {
let admin = get_admin_client().await?;
let mapper = ProtocolMapperRepresentation {
name: "custom-claim".to_string(),
protocol: Some("openid-connect".to_string()),
protocol_mapper: Some("oidc-usermodel-attribute-mapper".to_string()),
config: Some(
vec![
("user.attribute".to_string(), "customAttribute".to_string()),
("claim.name".to_string(), "custom_claim".to_string()),
("jsonType.label".to_string(), "String".to_string()),
("access.token.claim".to_string(), "true".to_string()),
("id.token.claim".to_string(), "true".to_string()),
].into_iter().collect()
),
..Default::default()
};
admin
.realm_clients_id_protocol_mappers_models_post(
"myrealm",
"myclient",
mapper
)
.await?;
Ok(())
}
2. 刷新令牌
async fn refresh_token(refresh_token: &str) -> Result<AccessTokenResponse, KeycloakError> {
let keycloak = Keycloak::new(
"http://localhost:8080/auth",
reqwest::Client::new()
);
let new_token = keycloak
.realm("myrealm")
.client("myclient")
.refresh_token(refresh_token)
.await?;
Ok(new_token)
}
最佳实践
- 缓存令牌: 避免频繁向Keycloak服务器请求
- 错误处理: 正确处理各种认证和授权错误
- 配置管理: 将Keycloak配置放在环境变量或配置文件中
- HTTPS: 生产环境始终使用HTTPS
- 定期轮换: 定期更换客户端密钥和证书
完整示例
下面是一个完整的Rust Web应用集成Keycloak的示例(使用actix-web框架):
use actix_web::{get, web, App, HttpServer, HttpResponse, Responder};
use keycloak::{Keycloak, types::AccessTokenResponse};
use std::collections::HashMap;
// 应用状态持有Keycloak客户端实例
struct AppState {
keycloak: Keycloak,
}
// 登录端点,重定向到Keycloak授权页面
#[get("/login")]
async fn login(data: web::Data<AppState>) -> impl Responder {
let auth_url = data.keycloak
.realm("myrealm")
.client("myclient")
.authorization_url()
.redirect_uri("http://localhost:8081/callback")
.build();
HttpResponse::Found()
.append_header(("Location", auth_url))
.finish()
}
// 回调端点,处理Keycloak返回的授权码
#[get("/callback")]
async fn callback(
data: web::Data<AppState>,
code: web::Query<HashMap<String, String>>,
) -> impl Responder {
let token = data.keycloak
.realm("myrealm")
.client("myclient")
.authorization_code_grant()
.authenticate(
code.get("code").unwrap(),
"http://localhost:8081/callback"
)
.await
.unwrap();
HttpResponse::Ok().json(token)
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
// 初始化Keycloak客户端
let keycloak = Keycloak::new(
"http://localhost:8080/auth",
reqwest::Client::new()
);
// 创建应用状态
let app_data = web::Data::new(AppState { keycloak });
// 启动HTTP服务器
HttpServer::new(move || {
App::new()
.app_data(app_data.clone())
.service(login)
.service(callback)
})
.bind("127.0.0.1:8081")?
.run()
.await
}
通过以上示例和说明,您可以在Rust应用中集成Keycloak实现安全的身份认证和授权功能。