繁体   English   中英

Keycloak:使用自定义JWT对用户进行身份验证

[英]Keycloak: Authenticate user with a custom JWT

情况:我们使用keycloak通过使用JavaScript适配器的普通浏览器身份验证流程来验证Web应用程序(A)中的用户。 这很完美!

目标:现在,新的用户组应该能够访问A.但是他们使用用户名和密码在受信任的第三方应用程序(B)中登录而没有Keycloak。 在B中,它们具有指向A的链接,其中包含自定义JWT(主要包含用户名和角色)作为查询参数。 因此,当用户点击链接时,他会登陆我们的应用程序入口点,在那里我们可以从URL中读取JWT。 现在需要做的是某种代币交换。 我们希望将此自定义JWT发送给Keycloak,后者会将访问令牌发送回正常登录过程。

问题: Keycloak中是否有针对此类用例的内置支持?

尝试:

我尝试使用“签名JWT”创建一个机密客户端作为“ 客户端身份验证器 ”,如文档中所建议的那样。 经过一些测试后,我认为这不是正确的轨道,即使名称很有希望。

另一个轨道是“ 客户建议的身份提供商 ”,通过实施自定义身份提供商。 但是我没有看到,我如何在请求中发送JWT。

目前我正在尝试使用Autentication SPI通过自定义身份验证器扩展身份验证流程。

也许它比我想象的要简单得多。 谁能引导我朝着正确的方向前进?

所以我终于能够使用问题中提到的Authentication SPI来解决它。

在Keycloak中,我制作了一份“浏览器”认证流程(因为你无法修改内置流程)并引入了一个额外的步骤“Portal JWT”(见下图)。 然后我将其绑定到“Bindings”选项卡中的“Browser Flow”

自定义验证流程

“Portal JWT”背后是我的自定义身份验证器,它从重定向uri中的查询参数中提取JWT并解析它以获取用户名和角色。 然后,用户将自定义属性“isExternal”添加到keycloak。 以下是它的摘录:

public class JwtAuthenticator implements Authenticator {

private final JwtReader reader;

JwtAuthenticator(JwtReader reader) {
    this.reader = reader;
}

@Override
public void authenticate(AuthenticationFlowContext context) {
    Optional<String> externalCredential = hasExternalCredential(context);
    if (externalCredential.isPresent()) {
        ExternalUser externalUser = reader.read(context.getAuthenticatorConfig(), externalCredential.get());
        String username = externalUser.getUsername();
        UserModel user = context.getSession().users().getUserByUsername(username, context.getRealm());
        if (user == null) {
            user = context.getSession().users().addUser(context.getRealm(), username);
            user.setEnabled(true);
            user.setSingleAttribute("isExternal", "true");
        }
        for (String roleName : externalUser.getRoles()) {
            RoleModel role = context.getRealm().getRole(roleName);
            if (role == null) {
                role = context.getRealm().addRole(roleName);
            }
            user.grantRole(role);
        }
        context.setUser(user);
        context.success();
    } else {
        context.attempted();
    }
}

private Optional<String> hasExternalCredential(AuthenticationFlowContext context) {
    String redirectUri = context.getUriInfo().getQueryParameters().getFirst("redirect_uri);
    try {
        List<NameValuePair> queryParams = URLEncodedUtils.parse(new URI(redirectUri), "UTF-8");
        Optional<NameValuePair> jwtParam = queryParams.stream()
                .filter(nv -> "jwt".equalsIgnoreCase(nv.getName())).findAny();
        if (jwtParam.isPresent()) {
            String jwt = jwtParam.get().getValue();
            if (LOG.isDebugEnabled()) {
                LOG.debug("JWT found: " + jwt);
            }
            return Optional.of(jwt);
        }
    } catch (URISyntaxException e) {
        LOG.error("Redirect URL not as expected: " + redirectUri);
    }
    return Optional.empty();
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM