7.7. 添加/删除用户和查询功能接口


我们没有通过我们的示例完成的一个操作是允许它添加和删除用户或更改密码。在我们的示例中定义的用户也可以在管理控制台中查询或查看。要添加这些增强功能,我们的示例供应商必须实施 UserQueryMethodsProvider (或 UserQueryProvider)和 UserRegistrationProvider 接口。

7.7.1. 实现 UserRegistrationProvider

使用这个流程实现从特定存储中添加和删除用户,我们首先需要将属性文件保存到磁盘。

PropertyFileUserStorageProvider

Copy to Clipboard Toggle word wrap
    public void save() {
        String path = model.getConfig().getFirst("path");
        path = EnvUtil.replace(path);
        try {
            FileOutputStream fos = new FileOutputStream(path);
            properties.store(fos, "");
            fos.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

然后,addUser ()removeUser () 方法的实现变得简单。

PropertyFileUserStorageProvider

Copy to Clipboard Toggle word wrap
    public static final String UNSET_PASSWORD="#$!-UNSET-PASSWORD";

    @Override
    public UserModel addUser(RealmModel realm, String username) {
        synchronized (properties) {
            properties.setProperty(username, UNSET_PASSWORD);
            save();
        }
        return createAdapter(realm, username);
    }

    @Override
    public boolean removeUser(RealmModel realm, UserModel user) {
        synchronized (properties) {
            if (properties.remove(user.getUsername()) == null) return false;
            save();
            return true;
        }
    }

请注意,在添加用户时,我们将属性映射的密码值设置为 UNSET_PASSWORD。我们这样做,因为我们没有属性值中属性的 null 值。我们还必须修改 CredentialInputValidator 方法以反映这一点。

如果提供程序实现了 UserRegistrationProvider 接口,则将调用 addUser () 方法。如果您的供应商有一个配置开关来为用户关闭,请从此方法返回 null 将跳过供应商并调用下一个供应商。

PropertyFileUserStorageProvider

Copy to Clipboard Toggle word wrap
    @Override
    public boolean isValid(RealmModel realm, UserModel user, CredentialInput input) {
        if (!supportsCredentialType(input.getType()) || !(input instanceof UserCredentialModel)) return false;

        UserCredentialModel cred = (UserCredentialModel)input;
        String password = properties.getProperty(user.getUsername());
        if (password == null || UNSET_PASSWORD.equals(password)) return false;
        return password.equals(cred.getValue());
    }

由于我们现在可以保存我们的属性文件,因此允许密码更新也有意义。

PropertyFileUserStorageProvider

Copy to Clipboard Toggle word wrap
    @Override
    public boolean updateCredential(RealmModel realm, UserModel user, CredentialInput input) {
        if (!(input instanceof UserCredentialModel)) return false;
        if (!input.getType().equals(PasswordCredentialModel.TYPE)) return false;
        UserCredentialModel cred = (UserCredentialModel)input;
        synchronized (properties) {
            properties.setProperty(user.getUsername(), cred.getValue());
            save();
        }
        return true;
    }

现在,我们还可以实施禁用密码。

PropertyFileUserStorageProvider

Copy to Clipboard Toggle word wrap
    @Override
    public void disableCredentialType(RealmModel realm, UserModel user, String credentialType) {
        if (!credentialType.equals(PasswordCredentialModel.TYPE)) return;
        synchronized (properties) {
            properties.setProperty(user.getUsername(), UNSET_PASSWORD);
            save();
        }

    }

    private static final Set<String> disableableTypes = new HashSet<>();

    static {
        disableableTypes.add(PasswordCredentialModel.TYPE);
    }

    @Override
    public Stream<String> getDisableableCredentialTypes(RealmModel realm, UserModel user) {

        return disableableTypes.stream();
    }

实施这些方法后,您现在可以更改并禁用管理控制台中用户的密码。

7.7.2. 实现 UserQueryProvider

UserQueryProviderUserQueryMethodsProviderUserCountMethodsProvider 的组合。如果没有实现 UserQueryMethodsProvider,则管理控制台将无法查看和管理我们示例供应商加载的用户。我们来了解如何实施此接口。

PropertyFileUserStorageProvider

Copy to Clipboard Toggle word wrap
    @Override
    public int getUsersCount(RealmModel realm) {
        return properties.size();
    }

    @Override
    public Stream<UserModel> searchForUserStream(RealmModel realm, String search, Integer firstResult, Integer maxResults) {
        Predicate<String> predicate = "*".equals(search) ? username -> true : username -> username.contains(search);
        return properties.keySet().stream()
                .map(String.class::cast)
                .filter(predicate)
                .skip(firstResult)
                .map(username -> getUserByUsername(realm, username))
                .limit(maxResults);
    }

searchForUserStream () 的第一个声明采用 String 参数。在本例中,参数代表您要搜索的用户名。此字符串可以是子字符串,它解释了执行搜索时 String.contains () 方法的选择。请注意,使用 * 表示请求所有用户的列表。该方法迭代属性文件的关键集合,委派为 getUserByUsername () 来加载用户。请注意,我们根据 firstResultmaxResults 参数索引此调用。如果您的外部存储不支持分页,则必须执行类似的逻辑。

PropertyFileUserStorageProvider

Copy to Clipboard Toggle word wrap
    @Override
    public Stream<UserModel> searchForUserStream(RealmModel realm, Map<String, String> params, Integer firstResult, Integer maxResults) {
        // only support searching by username
        String usernameSearchString = params.get("username");
        if (usernameSearchString != null)
            return searchForUserStream(realm, usernameSearchString, firstResult, maxResults);

        // if we are not searching by username, return all users
        return searchForUserStream(realm, "*", firstResult, maxResults);
    }

使用 Map 参数的 searchForUserStream () 方法可以根据名字、姓氏、用户名和电子邮件搜索用户。仅存储用户名,因此搜索仅基于用户名,除了 Map 参数不包含 username 属性时除外。在这种情况下,所有用户都会被返回。在这种情况下,使用 searchForUserStream (realm, search, firstResult, maxResults)

PropertyFileUserStorageProvider

Copy to Clipboard Toggle word wrap
    @Override
    public Stream<UserModel> getGroupMembersStream(RealmModel realm, GroupModel group, Integer firstResult, Integer maxResults) {
        return Stream.empty();
    }

    @Override
    public Stream<UserModel> searchForUserByUserAttributeStream(RealmModel realm, String attrName, String attrValue) {
        return Stream.empty();
    }

组或属性不会被存储,因此其他方法会返回空流。

返回顶部
Red Hat logoGithubredditYoutubeTwitter

学习

尝试、购买和销售

社区

关于红帽文档

通过我们的产品和服务,以及可以信赖的内容,帮助红帽用户创新并实现他们的目标。 了解我们当前的更新.

让开源更具包容性

红帽致力于替换我们的代码、文档和 Web 属性中存在问题的语言。欲了解更多详情,请参阅红帽博客.

關於紅帽

我们提供强化的解决方案,使企业能够更轻松地跨平台和环境(从核心数据中心到网络边缘)工作。

Theme

© 2025 Red Hat, Inc.