7.6. 사용자 및 쿼리 기능 인터페이스 추가/제거
예제에 포함되지 않은 한 가지는 사용자를 추가 및 제거하거나 암호를 변경할 수 있도록 허용하는 것입니다. 예제에 정의된 사용자도 Admin Console에서 쿼리하거나 볼 수 없습니다. 이러한 향상된 기능을 추가하려면 예제 공급자가 UserQueryProvider
및 UserRegistrationProvider
인터페이스를 구현해야 합니다.
7.6.1. Implementing UserRegistrationProvider
이 절차를 사용하여 특정 저장소에서 사용자 추가 및 제거를 구현하려면 먼저 속성 파일을 디스크에 저장할 수 있어야 합니다.
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; } }
사용자를 추가할 때 속성 맵의 암호 값을 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 Console에서 사용자의 암호를 변경하고 비활성화할 수 있습니다.
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()이 사용자를 로드하기 위해 속성 파일의 키 집합을 반복하고 getUserByUsername()
에 위임합니다. firstResult
및 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()
메서드는 첫 번째, 성, 사용자 이름, 이메일에 따라 사용자를 검색할 수 있습니다. 사용자 이름만 저장하므로 사용자 이름을 기반으로만 검색합니다. 이를 위해 searchForUser()
에 위임합니다.
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; }
그룹 또는 특성을 저장하지 않으므로 다른 방법으로는 빈 목록이 반환됩니다.