7.8. 구현 전략 가져오기
사용자 스토리지 공급자를 구현할 때 수행할 수 있는 또 다른 전략이 있습니다. 사용자 페더레이션 스토리지를 사용하는 대신 Red Hat Single Sign-On 내장 사용자 데이터베이스에 로컬로 사용자를 생성하고 외부 저장소의 속성을 이 로컬 복사본으로 복사할 수 있습니다. 이 접근 방식에는 많은 이점이 있습니다.
- Red Hat Single Sign-On은 기본적으로 외부 저장소의 지속성 사용자 캐시가 됩니다. 사용자를 가져오면 더 이상 외부 저장소에 충돌하지 않으므로 로드를 수행하지 않습니다.
- 공식 사용자 저장소로 Red Hat Single Sign-On으로 이동하여 이전 외부 저장소를 사용 중단하는 경우 Red Hat Single Sign-On을 사용하도록 애플리케이션을 천천히 마이그레이션할 수 있습니다. 모든 애플리케이션이 마이그레이션되면 가져온 사용자를 분리하고 기존의 기존 외부 저장소를 중단시킵니다.
가져오기 전략을 사용하는 데는 몇 가지 명확한 문제가 있습니다.
- 처음 사용자를 찾으려면 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()
메서드를 호출하여 로컬 Red Hat Single Sign-On 사용자 스토리지에 대한 참조를 가져옵니다. 사용자가 로컬에 저장되었는지 여부를 확인할 수 있습니다. 그렇지 않은 경우 로컬에 추가합니다. 로컬 사용자의 ID 를
설정하지 마십시오. Red Hat Single Sign-On에서 ID를 자동으로 생성하도록 합니다
. 또한 UserModel.setFederationLink()
를 호출하고 공급자의 ComponentModel
ID를 전달합니다. 이렇게 하면 공급자와 가져온 사용자 간 링크가 설정됩니다.
사용자 스토리지 공급자가 제거되면 가져온 모든 사용자도 제거됩니다. 이는 UserModel.setFederationLink()
를 호출하기 위한 목적 중 하나입니다.
또 다른 점은 로컬 사용자가 연결되어 있는 경우 스토리지 공급자가 CredentialInputValidator
및 CredentialInputUpdater
인터페이스에서 구현하는 메서드에 여전히 위임된다는 것입니다. 검증 또는 업데이트에서 false
를 반환하면 로컬 스토리지를 사용하여 검증하거나 업데이트할 수 있는지 Red Hat Single Sign-On이 표시됩니다.
또한 org.keycloak.models.utils.UserModelForwarded 클래스를 사용하여 로컬 사용자를 프록시하고
있습니다. 이 클래스는 UserModel
의 구현입니다. 모든 메서드는 인스턴스화한 UserModel
에 위임합니다. 이 delegate 클래스의 setUsername()
메서드를 재정의하여 속성 파일과 자동으로 동기화합니다. 공급자의 경우 이를 사용하여 로컬 UserModel
의 다른 메서드를 가로채 어 외부 저장소와의 동기화를 수행할 수 있습니다. 예를 들어, get 메서드가 로컬 저장소가 동기화되어 있는지 확인할 수 있습니다. 설정 방법은 로컬 저장소와 동기화된 외부 저장소를 유지합니다. 한 가지 주목할 점은 getId()
메서드에서 사용자를 로컬로 생성할 때 자동 생성된 ID를 항상 반환해야 한다는 것입니다. 다른 중요하지 않은 예에 표시된 대로 페더레이션 ID를 반환해서는 안 됩니다.
공급자가 UserRegistrationProvider
인터페이스를 구현하는 경우 removeUser()
메서드가 로컬 스토리지에서 사용자를 제거할 필요가 없습니다. 런타임은 이 작업을 자동으로 수행합니다. 또한 removeUser()
는 로컬 스토리지에서 제거되기 전에 호출됩니다.
7.8.1. ImportedUserValidation 인터페이스
이 장의 앞부분을 기억하는 경우 사용자 쿼리가 어떻게 작동하는지에 대해 설명합니다. 사용자가 있는 경우 로컬 스토리지를 먼저 쿼리한 다음 쿼리가 종료됩니다. 이는 로컬 UserModel
을 프록시하여 사용자 이름을 동기화 상태로 유지할 수 있도록 위의 구현에서 문제가 됩니다. User Storage 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
이 사용될 예정입니다. 선택적으로 검사를 수행하여 사용자가 외부 저장소에 여전히 존재하는지 확인할 수도 있습니다. 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()
메서드를 호출합니다.