7.6. 添加/删除用户并查询功能接口
通过我们的示例,我们尚未完成一件事情是,允许它添加和删除用户或更改密码。示例中定义的用户还无法在管理控制台中查询或查看。要添加这些增强功能,我们的示例供应商必须实施 UserQueryProvider 和 UserRegistrationProvider 接口。
7.6.1. 实施用户RegistrationProvider 复制链接链接已复制到粘贴板!
使用这个步骤实施从特定存储中添加和删除用户,我们首先需要将此属性文件保存到磁盘中。
PropertyFileUserStorageProvider
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
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;
}
}
请注意,在添加用户时,我们将属性映射的 password 值设置为 UNSET_PASSWORD。我们这样做,因为我们不能对属性值中的属性具有 null 值。我们还必须修改 CredentialInputValidator 方法以反映这一点。
如果提供程序实施 UserRegistrationProvider 接口,则将调用 addUser () 方法。如果您的供应商有配置开关来关闭添加用户,则从此方法返回 null 将跳过该提供程序并调用下一个用户。
PropertyFileUserStorageProvider
@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
@Override
public boolean updateCredential(RealmModel realm, UserModel user, CredentialInput input) {
if (!(input instanceof UserCredentialModel)) return false;
if (!input.getType().equals(CredentialModel.PASSWORD)) return false;
UserCredentialModel cred = (UserCredentialModel)input;
synchronized (properties) {
properties.setProperty(user.getUsername(), cred.getValue());
save();
}
return true;
}
现在,我们可以实施禁用密码。
PropertyFileUserStorageProvider
@Override
public void disableCredentialType(RealmModel realm, UserModel user, String credentialType) {
if (!credentialType.equals(CredentialModel.PASSWORD)) return;
synchronized (properties) {
properties.setProperty(user.getUsername(), UNSET_PASSWORD);
save();
}
}
private static final Set<String> disableableTypes = new HashSet<>();
static {
disableableTypes.add(CredentialModel.PASSWORD);
}
@Override
public Set<String> getDisableableCredentialTypes(RealmModel realm, UserModel user) {
return disableableTypes;
}
通过实施这些方法,您现在可以在 Admin 控制台中更改和禁用用户的密码。
7.6.2. 实施 UserQueryProvider 复制链接链接已复制到粘贴板!
如果不实施 UserQueryProvider,管理控制台将无法查看和管理我们的示例提供者加载的用户。我们来看一下实施此界面。
PropertyFileUserStorageProvider
@Override
public int getUsersCount(RealmModel realm) {
return properties.size();
}
@Override
public List<UserModel> getUsers(RealmModel realm) {
return getUsers(realm, 0, Integer.MAX_VALUE);
}
@Override
public List<UserModel> getUsers(RealmModel realm, int firstResult, int maxResults) {
List<UserModel> users = new LinkedList<>();
int i = 0;
for (Object obj : properties.keySet()) {
if (i++ < firstResult) continue;
String username = (String)obj;
UserModel user = getUserByUsername(username, realm);
users.add(user);
if (users.size() >= maxResults) break;
}
return users;
}
getUsers () 方法迭代属性文件的密钥集,委派为 getUserByUsername () 来加载用户。请注意,我们根据 第一个 结果和 maxResults 参数索引了这个调用。如果您的外部存储不支持分页,则您必须执行类似的逻辑。
PropertyFileUserStorageProvider
@Override
public List<UserModel> searchForUser(String search, RealmModel realm) {
return searchForUser(search, realm, 0, Integer.MAX_VALUE);
}
@Override
public List<UserModel> searchForUser(String search, RealmModel realm, int firstResult, int maxResults) {
List<UserModel> users = new LinkedList<>();
int i = 0;
for (Object obj : properties.keySet()) {
String username = (String)obj;
if (!username.contains(search)) continue;
if (i++ < firstResult) continue;
UserModel user = getUserByUsername(username, realm);
users.add(user);
if (users.size() >= maxResults) break;
}
return users;
}
searchForUser () 的第一个声明采用 String 参数。这应该是字符串,用于搜索用户名和电子邮件属性来查找用户。这个字符串可以是子字符串,这也是我们在进行搜索时使用 String.contains () 方法的原因。
PropertyFileUserStorageProvider
@Override
public List<UserModel> searchForUser(Map<String, String> params, RealmModel realm) {
return searchForUser(params, realm, 0, Integer.MAX_VALUE);
}
@Override
public List<UserModel> searchForUser(Map<String, String> params, RealmModel realm, int firstResult, int maxResults) {
// only support searching by username
String usernameSearchString = params.get("username");
if (usernameSearchString == null) return Collections.EMPTY_LIST;
return searchForUser(usernameSearchString, realm, firstResult, maxResults);
}
采用 Map 参数的 searchForUser () 方法可以根据第一个、姓、用户名和电子邮件搜索用户。我们只存储用户名,因此我们只根据用户名进行搜索。我们委派给 搜索ForUser ()。
PropertyFileUserStorageProvider
@Override
public List<UserModel> getGroupMembers(RealmModel realm, GroupModel group, int firstResult, int maxResults) {
return Collections.EMPTY_LIST;
}
@Override
public List<UserModel> getGroupMembers(RealmModel realm, GroupModel group) {
return Collections.EMPTY_LIST;
}
@Override
public List<UserModel> searchForUserByUserAttribute(String attrName, String attrValue, RealmModel realm) {
return Collections.EMPTY_LIST;
}
我们不存储组或属性,因此其他方法会返回一个空列表。