当前位置: 首页 > 知识库问答 >
问题:

Keycloak用户存储SPI身份验证流

柴瀚
2023-03-14

我已经实现了keycloak用户存储SPI流。它针对外部源进行身份验证。Keycloak是用外部存储属性更新的,如果它在getUserByUsername方法中更新,它就可以工作。

我正在isValid方法中验证userValidation,并更新UserModel和LocalHashMap中的userAttributes。

从日志中,我看到UserStoreProviderFactory创建了两个实例来验证一个事务/登录

为什么在第一次userStorageProvider调用时多次调用getUserByUserName()方法

            UserStorageProviderFactory
                 -> Creates instance of UserStorageProvider
                    -> UserStorage provider calls getUserById method
                    -> UserStorage provider calls getUserByUserName method
                    -> UserStorage provider calls isValid method
                                 on true, keycloak 
            UserStorageProviderFactory
                 -> Creates instance of UserStorageProvider
                    -> UserStorage provider calls getUserById method
                    -> UserStorage provider calls getUserByUserName method
                           returns UserModel


 @Override
public UserModel getUserById(String id, RealmModel realm) {
    logger.debug("__ ########## getUserById  ID:" + "ID: " + id + ":REALM:" + realm);
    StorageId storageId = new StorageId(id);
    /**
     * StorageId.getExternalId() method is invoked to obtain 
     * the username embeded in the id parameter
     */
    String username = storageId.getExternalId();
    System.out.println("Name:" + username);
    return getUserByUsername(username, realm);
}

/***
 * From UserLookupProvider
 * This method is invoked by the Keycloak login page when a user logs in
 */
@Override
public UserModel getUserByUsername(String username, RealmModel realm) {
    logger.debug("__ %%%%%%%%%% getUserByUsername ID:" + username + ":REALM:" + realm);
    logger.debug("Provider Num:" + this.randomNum);
    UserModel userModel = loadedUsers.get(username);

    UserModel local = session.userLocalStorage().getUserByUsername(username, realm);
    logger.debug("*#@#$ local session:" + local);

    if (userModel == null) {
        logger.debug("In NULLLLLLLLLLLLLLLLLLL ");
        userModel = createUserModel(realm, username);
        // set for demo...
        userModel.setSingleAttribute("externalId", Integer.toString(10));
    } else {
        logger.debug("__ ELSEEEEEEEEEEEEEE getUserByUsername sku:" +  userModel.getFirstAttribute("sku"));
        userModel.setSingleAttribute("externalId", Integer.toString(12));
    }

    return userModel;
}

public boolean isValid(RealmModel realm, UserModel user, CredentialInput input) {
    logger.debug("__ , 1111111111 isValid:input type:" +  input.getType() + ":userName:" + user.getUsername());

    UserCredentialModel cred = (UserCredentialModel)input;

    if (!supportsCredentialType(input.getType()) 
            || !(input instanceof UserCredentialModel)) {
        logger.warn("__, credentialType: " + input.getType() + ": NOT supported");
        return false;
    }

    UserDTO model = new UserDTO();
    model.setEmailId(user.getUsername());
    model.setPassword(cred.getValue());

    try {
        ResteasyClient client = new ResteasyClientBuilder().build();
        ResteasyWebTarget target = client.target(properties.getProperty("userUrl"));
        Response response = target.request().post(
                Entity.entity(model, "application/json"));

        ResponseDTO<UserResponseDTO> responseDto = response.readEntity(new GenericType<ResponseDTO<UserResponseDTO>>() {});
        logger.debug("__ , !!!!!!!!!!! json response:" +  responseDto);
        logger.debug("!!!! Provider Num:" + this.randomNum);
        if (responseDto.getCode() == 200) {
            UserResponseDTO userResponseDto = responseDto.getData();
            UserDetailsDTO userDetailsDto = userResponseDto.getDetails();
            user.setEmail(userDetailsDto.getEmailId());
            user.setFirstName(userDetailsDto.getFirstName());

            logger.debug("!!!!@@@@@@@@ Provider Num:" + this.randomNum);
            logger.debug("__ , returning success from isValid:" + user.getUsername() +":::"); 
            loadedUsers.put(user.getUsername(), user);
            return true;
        }
        response.close();

    } catch(Exception ex) {
        logger.error("Exception Occured accessing:", ex);
    }

    return false;

}

日志(为了显示调用getUserById()和getUserByUserName()方法的次数,UserStorageFactory实例化了两次UserStorageProvider实例)

2019-08-30 08:49:26,273调试[org.test.keycloak.userstore.factory.TestUserStoreProviderFactory](默认任务-2)用户URL:http://localhost:8280/login

2019-08-30 08:49:26,960 DEBUG[org.test.keycloak.userstore.testUserStoreProvider](默认任务-2)__%%%%%%%%%%%getUserByUsername ID:test@gmail.com:realm:org.keycloak.models.cache.infinispan.realmAdapter@64DDDA7a 2019-08-30 08:49:26,962 DEBUG[org.test.keycloak.userstore.testUserStoreProvider](默认任务-2)LOADEDUSERS,TestUserStoreProvider](默认任务-2)在NulllLlLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL2019-08-30 08:49:26,965 DEBUG[org.test.keycloak.userstore.testUserStoreProvider](默认任务-2)__中,从isvalid:test@gmail.com:::

2019-08-30 08:49:26,966 DEBUG[org.test.keycloak.userstore.testUserStoreProvider](默认任务-2)__################getUserById ID:ID:F:4A65AC95-6B5D-4508-B7EC-348AE1DA619D:test@gmail.com:realm:org.keycloak.models.cache.infinispan.realmadapter@64DDDA7A 2019-08-30 08:49:26,986 DEBUG%%%%getUserByUsername ID:test@gmail.com:realm:org.keycloak.models.cache.infinispan.realmAdapter@64DDDA7a 2019-08-30 08:49:27,022 DEBUG[org.test.keycloak.userstore.testUserstoreProvider](默认任务

2019-08-30 08:49:27,056 DEBUG[org.test.keycloak.userstore.testUserStoreProvider](默认任务-2)__###############getUserById ID:ID:F:4A65AC95-6B5D-4508-B7EC-348AE1DA619D:test@gmail.com:realm:org.keycloak.models.cache.infinispan.realmadapter@64DDDA7A 2019-08-30 08:49:27,059 DEBUG%%%%getUserByUsername ID:test@gmail.com:realm:org.keycloak.models.cache.infinispan.realmadapter@64DDDA7A 2019-08-30 08:49:27,059 DEBUG[org.test.keycloak.userstore.testUserStoreProvider](默认任务-2)LOADEDUSERS,keysize:1 201 9-08-30 08:49:27,059 DEBUG[org.test.keycloak.userstore.testUserStoreProvider](默认任务-2)test.keycloak.userstore.testUserStoreProvider](默认任务-2)__%%%%%%%%%%%getUserByUsername id:test@gmail.com:realm:org.keycloak.models.cache.infinispan.realmAdapter@64DDDA7a

2019-08-30 08:49:27,071 DEBUG[org.test.keycloak.userstore.testUserStoreProvider](默认任务-2)__###############getUserById ID:ID:F:4A65AC95-6B5D-4508-B7EC-348AE1DA619D:test@gmail.com:realm:org.keycloak.models.cache.infinispan.realmadapter@64DDDA7A 2019-08-30 08:49:27,073 DEBUG%%%%getUserByUsername ID:test@gmail.com:realm:org.keycloak.models.cache.infinispan.realmAdapter@64DDDA7A 2019-08-30 08:49:27,073 DEBUG[org.test.keycloak.userstore.testUserstoreProvider](默认任务-2)__ELSEEEEEEEEEEEEEE getUserByUsername sku:1

2019-08-30 08:49:29,421调试[org.test.keycloak.userstore.factory.testuserstoreProviderFactory](默认任务-2)用户URL:http://localhost:8280/login 2019-08-30 08:49:29,422调试[org.test.keycloak.userstore.testuserstoreProvider](默认任务-2).infinispan.realmadapter@64DDDA7A 2019-08-30 08:49:29,432 DEBUG[org.test.keycloak.userstore.testuserstoreProvider](默认任务-2)__%%%%%%%%%%getUserByUsername id:test@gmail.com:realm:org.keycloak.models.cache.infinispan.realmadapter@64DDDA7A 2019-08-30 08:49:29,432 DEBUG[org.test.keycloak.userstore.testuserstoreProvider](默认任务:29,581 DEBUG[org.test.keycloak.userstore.testUserStoreProvider](默认任务-2)__############getUserById ID:ID:F:4A65AC95-6B5D-4508-B7EC-348AE1DA619D:test@gmail.com:realm:org.keycloak.models.cache.infinispan.realmAdapter@64DDDA7A 2019-08-3008:49:29,581 DEBUG[org.test.keycloak.userstore.testUserStoreProvider](默认任务-2)__%%%%%%%%%%getUserByUsername id:test@gmail.com:realm:org.keycloak.models.cache.infinispan.realmAdapter@64DDDA7a

任何建议/提示,你可以帮助我解决/理解工作流程将非常感谢!

共有1个答案

长孙深
2023-03-14

>

  • 首先,创建一个UserProvider实例来通过登录流。在此流上更新的UserModel在外部不可用,除非它被缓存/存储

    其次,使用下面的endpoint创建一个UserProvider实例来创建令牌。UserModel对象从getUserByUserName方法()返回。“http://localhost:8180/auth/realms/myapp/protocol/openid-connect/token”

    若要存储具有自定义属性的UserModel,

    this.session.userLocalStorage().addUser(realm, user.getUsername());
    
        @Override
    public UserModel addUser(RealmModel realm, String username) {
        // userModel returned should be subclassed or instance of AbstractUserAdapterFederatedStorage
        return this.myUserModel;
    
    }
    
    //ComponentModel ID of the provider. This sets a link between the provider and the imported user
    user.setFederationLink(this.model.getId());
    

  •  类似资料:
    • 我正在尝试实现一个自定义密钥克拉克身份验证器SPI,用于针对外部数据源/REST服务进行身份验证。计划是将它们迁移到Keycloak。 成功后,在keycloak数据源上创建用户。 创建自定义映射器以在令牌上添加额外的用户属性。 我正在遵循官方指南https://www.keycloak.org/docs/latest/server_development/index.html#_auth_spi

    • 我遵循了为keycloak(版本4.8.3)设置自定义身份验证器spi的演练。我几乎只使用从这里得到的示例代码。我只更改了以便能够编译项目并使用部署它。而且它起作用了...我可以在keycloak中配置新的身份验证流,更新浏览器流并设置所需的动作。但是,如果我想在应用程序中使用新的身份验证,我会得到以下消息:。并且在控制台中得到以下输出: 我在github上查找了文件DefaulTauthenti

    • 我试图解决的用例是 用户将显示keycloak登录屏幕。Onsubmission用户将根据外部数据源验证。 从外部数据源中检索一些属性,将其映射到KeyCloak的id和访问令牌。 我所做的是从实现自定义身份验证器SPI开始,认为这不是正确的方法,现在实现了UserStorageProvider。 遵循文档(https://www.keycloak.org/docs/latest/server_d

    • 我尝试将安装程序部署到本地存储库: --设置。xml-- --波姆。xml--- 当我执行“mvn部署”时,我收到错误: 艺术家日志: 如果我将服务器/存储库id更改为“myserver.com”-部署工作!但这不适合我,因为在myserver上,这不适用于svn。通用域名格式 我试图将标签“配置文件”和“镜像”添加到server.xml和“分发管理”添加到pom.xml-得到相同的错误 服务器/

    • 在过去的几周里,我一直在努力掌握KeyClope,这样我就可以实现一种使用遗留提供商(基于Oracle,带有会话表和各种奇怪的东西)对用户进行身份验证的方法。我们计划在不久的将来解决这个问题,但目前我们必须解决这个问题,所以我们的想法是在前线使用KeyClope——利用它提供的主要好处,比如SSO——在需要身份验证的应用程序中省略传统的身份验证提供程序。 我读了一些关于构建自定义OIDC身份提供程

    • 于是我在这里看到:https://firebase . Google . com/docs/auth/web/account-linking # link-auth-provider-credentials-to-a-user-account现在可以在Firebase中链接用户账号了。我还看到Firebase提供了匿名认证的功能,它为一个用户创建一个用户会话,不需要任何凭证。 在我们的应用程序中,