今天,我们将深入了解 Keycloak,亲自动手进行自定义映射、扩展和集成。系好安全带,因为这次旅程将会非常刺激!

总结

我们将深入探讨 Keycloak 的自定义,包括:

  • 自定义用户属性映射
  • 构建 Keycloak 扩展(SPI)
  • 实现自定义认证流程
  • 与外部系统集成

如果你准备好将 Keycloak 变成你的个人认证游乐场,那就继续阅读吧!

自定义用户属性映射:因为一刀切并不适合所有人

说实话,Keycloak 中的默认用户属性就像香草冰淇淋一样无趣。但别担心!我们可以通过自定义属性映射来增加趣味性。

添加自定义属性

首先,让我们为用户添加一个自定义属性。在 Keycloak 管理控制台中:

  1. 导航到 用户 > 属性
  2. 添加一个新属性,比如 "favoriteProgLanguage"

现在,让我们将其映射到 JWT 令牌中的声明。前往 客户端 > [你的客户端] > 客户端范围 > 专用范围 > 添加映射器 > 通过配置 > 用户属性。

填写详细信息:

  • 名称:Favorite Programming Language
  • 用户属性:favoriteProgLanguage
  • 令牌声明名称:fav_lang

完成!现在,当用户登录时,他们的 JWT 将包含他们最喜欢的编程语言。为什么不呢?

自定义协议映射器

但等等,还有更多!如果我们想在属性进入令牌之前进行一些高级处理呢?这时就需要自定义协议映射器。

让我们创建一个映射器,将我们最喜欢的语言转换为大写(因为大声表达我们对 Python 的热爱是完全正常的)。


public class UppercaseLanguageMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, UserInfoTokenMapper {

    public static final String PROVIDER_ID = "uppercase-language-mapper";

    @Override
    public String getDisplayCategory() {
        return TOKEN_MAPPER_CATEGORY;
    }

    @Override
    public String getDisplayType() {
        return "Uppercase Language Mapper";
    }

    @Override
    public String getId() {
        return PROVIDER_ID;
    }

    @Override
    protected void setClaim(IDToken token, ProtocolMapperModel mappingModel, UserSessionModel userSession) {
        UserModel user = userSession.getUser();
        String favLang = user.getFirstAttribute("favoriteProgLanguage");
        if (favLang != null) {
            token.getOtherClaims().put("FAV_LANG", favLang.toUpperCase());
        }
    }
}

这就是我所说的自定义映射!你的最爱语言将从屋顶(或至少从你的 JWT)大声喊出。

Keycloak 扩展:因为有时候你需要打破规则

Keycloak 的可扩展性就像是认证爱好者的游乐场。让我们构建一个简单的 SPI(服务提供者接口),为认证流程添加自定义操作。

创建自定义认证器

想象一下,我们想要添加一个认证器来检查用户的密码是否包含他们的用户名(这是一个安全禁忌)。这是一个简化版本:


public class UsernamePasswordValidator implements Authenticator {

    @Override
    public void authenticate(AuthenticationFlowContext context) {
        UserModel user = context.getUser();
        CredentialInput input = context.getHttpRequest().getDecodedFormParameters().getFirst(CredentialForm.PASSWORD);
        
        if (input.getValue().contains(user.getUsername())) {
            context.failureChallenge(AuthenticationFlowError.INVALID_CREDENTIALS, 
                context.form().setError("passwordContainsUsername").createErrorPage());
            return;
        }
        
        context.success();
    }

    // ... other required methods
}

现在,你需要在 Keycloak 中注册这个认证器并将其添加到你的认证流程中。你的用户稍后会感谢你(或者现在就诅咒你,这很难说)。

自定义认证逻辑:因为有时候你需要成为一个特别的存在

假设你的公司有一种超级秘密的认证方法,涉及一个魔法 8 球和一杯咖啡。别担心,Keycloak 会支持你!

实现自定义认证流程

以下是自定义认证流程可能的样子:


public class CoffeeAuthenticator implements Authenticator {

    @Override
    public void authenticate(AuthenticationFlowContext context) {
        if (isCoffeeBrewed() && magicBallSaysYes()) {
            context.success();
        } else {
            context.failureChallenge(AuthenticationFlowError.INVALID_CREDENTIALS, 
                context.form().setError("needMoreCoffee").createErrorPage());
        }
    }

    private boolean isCoffeeBrewed() {
        // 实现你的咖啡检查逻辑
        return true;  // 乐观主义者,团结起来!
    }

    private boolean magicBallSaysYes() {
        // 咨询魔法 8 球
        return Math.random() > 0.5;  // 50/50 的机会,似乎很合理
    }

    // ... other required methods
}

记住,能力越大,责任越大。明智地使用这些知识,否则你可能会得到一个只在咖啡用完之前有效的认证系统。

与外部系统集成:与他人友好相处

Keycloak 不必是孤狼。让我们看看如何让它与外部系统友好相处。

自定义用户存储提供者

想象一下,你有用户数据存储在一个使用打孔卡的遗留系统中(因为为什么不呢?)。以下是如何将其与 Keycloak 集成的简化示例:


public class PunchCardUserStorageProvider implements UserStorageProvider, UserLookupProvider, CredentialInputValidator {

    private PunchCardSystem punchCardSystem;

    @Override
    public UserModel getUserByUsername(String username, RealmModel realm) {
        PunchCardUser punchCardUser = punchCardSystem.findUserByHoles(username);
        if (punchCardUser != null) {
            return new UserAdapter(session, realm, model, punchCardUser);
        }
        return null;
    }

    @Override
    public boolean isValid(RealmModel realm, UserModel user, CredentialInput input) {
        if (!supportsCredentialType(input.getType())) return false;

        return punchCardSystem.validateCard(user.getUsername(), input.getChallengeResponse());
    }

    // ... other required methods
}

现在,你的复古打孔卡系统可以在 Keycloak 中认证用户。欢迎来到 21 世纪……某种程度上。

总结:Keycloak 是你的珍珠

我们只是触及了 Keycloak 自定义的表面。从调整用户属性到构建完整的认证提供者,Keycloak 提供了一个强大的平台,让你可以根据自己的心意定制认证系统。

记住,能力越大,责任越大(以及可能的头痛)。彻底测试,认真记录,愿认证之神永远眷顾你。

关键要点:

  • 自定义属性映射让你的令牌更具特色
  • SPI 打开了可扩展性的世界
  • 自定义认证流程允许独特(且可能是咖啡驱动的)认证逻辑
  • 即使是最古老的系统,也可以进行外部系统集成

现在去定制吧!记住,如果有人问你为什么花了一周时间实现一个基于咖啡的认证系统,就告诉他们这是为了“增强安全性”。他们一定会相信的。

“做伟大工作的唯一方法就是热爱你所做的事情。” - 史蒂夫·乔布斯

(他可能不是在谈论 Keycloak 自定义,但我们可以假装。)

编码愉快,愿你的令牌永远有效,咖啡永远浓郁!