Rust身份认证与授权库keycloak的使用:集成Keycloak实现安全的单点登录和访问控制

Rust身份认证与授权库keycloak的使用:集成Keycloak实现安全的单点登录和访问控制

法律声明

本库采用双重许可协议:MITUNLICENSE

特性

实现了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.10013.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)
}

最佳实践

  1. 缓存令牌: 避免频繁向Keycloak服务器请求
  2. 错误处理: 正确处理各种认证和授权错误
  3. 配置管理: 将Keycloak配置放在环境变量或配置文件中
  4. HTTPS: 生产环境始终使用HTTPS
  5. 定期轮换: 定期更换客户端密钥和证书

完整示例

下面是一个完整的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实现安全的身份认证和授权功能。

回到顶部