7.8. 导入实施策略

在实施用户存储供应商时,可以采取的另一策略。您可以在 Red Hat Single Sign-On 内置用户数据库中本地创建用户,并将属性从外部存储中复制到此本地副本中。这种方法有很多优点。

  • 红帽单点登录基本上成为外部存储的持久性用户缓存。导入用户后,您将不再通过外部存储来减少负载。
  • 如果您要作为您的官方用户商店使用 Red Hat Single Sign-On 并弃用旧的外部存储,您可以慢慢地迁移应用程序以使用 Red Hat Single Sign-On。迁移完所有应用程序后,取消链接导入的用户,并停用旧的传统外部存储。

使用导入策略有一些明显的缺点:

  • 第一次查找用户时,需要多个更新红帽单点登录数据库。这可能会给负载带来巨大性能损失,并给红帽单点登录数据库带来大量负担。用户联合存储方法将仅根据需要存储额外的数据,且永远不会根据外部存储的功能使用。
  • 通过导入方法,您必须保持本地红帽单点登录存储和外部存储同步。User Storage SPI 有功能接口来支持同步,但这可能会很快变得困难和我。

要实施导入策略,只需检查第一个用户是否在本地导入该策略。如果返回本地用户,如果没有在本地创建用户并从外部存储导入数据。您还可以代理本地用户,以便自动同步大多数更改。

这将是一个位表述,但我们扩展了 PropertyFileUserStorageProvider 来采用这种方法。首先,我们首先修改 createAdapter () 方法。

PropertyFileUserStorageProvider

    protected UserModel createAdapter(RealmModel realm, String username) {
        UserModel local = session.userLocalStorage().getUserByUsername(username, realm);
        if (local == null) {
            local = session.userLocalStorage().addUser(realm, username);
            local.setFederationLink(model.getId());
        }
        return new UserModelDelegate(local) {
            @Override
            public void setUsername(String username) {
                String pw = (String)properties.remove(username);
                if (pw != null) {
                    properties.put(username, pw);
                    save();
                }
                super.setUsername(username);
            }
        };
    }

在此方法中,我们调用 KeycloakSession.userLocalStorage () 方法来获取对本地红帽单点登录用户存储的引用。我们可以看到用户是否存储在本地,我们是否在本地添加它。不要设置本地用户的 id。让 Red Hat Single Sign-On 会自动生成 id。另请注意,我们称之为 UserModel.setFederationLink (),并传递我们供应商的 ComponentModel 的 ID。这会在供应商和导入的用户之间设置链接。

注意

删除用户存储供应商时,其导入的任何用户也将被删除。这是调用 UserModel.setFederationLink () 的目的之一。

要注意的一点是,如果本地用户链接,您的存储供应商仍将委派给它从 CredentialInputValidatorCredentialInputUpdater 接口实施的方法。从验证或更新返回 false 只会使 Red Hat Single Sign-On 观察到是否使用本地存储进行验证或更新。

另请注意,我们使用 org.keycloak.models.utils.UserModelDelegate 类代理本地用户。这个类是 UserModel 的实现。每个方法都只委托给 UserModel,使用.我们将覆盖此委派类的 setUsername () 方法,以便自动与属性文件同步。对于您的供应商,您可以使用此方法 截获 本地 UserModel 上的其他方法,以便与外部存储执行同步。例如,get 方法可确保本地存储处于同步状态。设置方法使外部存储与本地存储保持同步。需要注意的是,getId () 方法应始终返回您在本地创建用户时自动生成的 id。您不应该返回联合 ID,如其它非导入示例所示。

注意

如果您的供应商实施 UserRegistrationProvider 接口,则 removeUser () 方法不需要从本地存储中删除该用户。运行时将自动执行此操作。另请注意,removeUser () 将在从本地存储中删除之前调用。

7.8.1. ImportedUserValidation 接口

如果您记得在本章前面部分,我们讨论如何查询用户。如果用户已找到,则会首先查询本地存储,然后查询结束。因为我们需要代理本地用户 Model,所以我们可能会使用户名保持同步。每当从本地数据库加载链接的本地用户时,用户存储 SPI 具有回调。

package org.keycloak.storage.user;
public interface ImportedUserValidation {
    /**
     * If this method returns null, then the user in local storage will be removed
     *
     * @param realm
     * @param user
     * @return null if user no longer valid
     */
    UserModel validate(RealmModel realm, UserModel user);
}

加载链接的本地用户时,如果用户存储提供程序类实现了这个接口,则将调用 validate () 方法。此处,您可以代理 作为参数传递的本地用户,并返回它。将使用新的 UserModel。您还可以选择检查以查看用户是否仍然在外部存储中。if validate () 返回 null,则本地用户将从数据库中删除。

7.8.2. ImportSynchronization 接口

通过导入策略,您可以看到本地用户副本可以和外部存储同步。例如,一个用户可能已从外部存储中删除。User Storage SPI 有另外一个界面,您可以实现这个界面,以便处理这个接口 org.keycloak.storage.user.ImportSynchronization:

package org.keycloak.storage.user;

public interface ImportSynchronization {
    SynchronizationResult sync(KeycloakSessionFactory sessionFactory, String realmId, UserStorageProviderModel model);
    SynchronizationResult syncSince(Date lastSync, KeycloakSessionFactory sessionFactory, String realmId, UserStorageProviderModel model);
}

此接口由供应商工厂实施。旦此界面由提供程序工厂实施,供应商的管理控制台管理页面会显示附加选项。您可以点击按钮手动强制同步。这会调用 ImportSynchronization.sync () 方法。另外还会显示额外的配置选项,供您自动调度同步。自动同步调用 syncSince () 方法。