簡體   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