The keycloak admin url invoke when client user send a logout request to it.I find that KeycloakAutoConfiguration#getKeycloakContainerCustomizer() inject WebServerFactoryCustomizer for add KeycloakAuthenticatorValve, and that Valve
use CatalinaUserSessionManagement, but it have not any info about redis as its session store. So I add a customizer for enhence the Valve.
- first i set the order of the autoconfig, because extra customizer must be callback after it.
@Slf4j
@Component
public class BeanFactoryOrderWrapper implements DestructionAwareBeanPostProcessor {
@Override
public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {
}
@Override
public boolean requiresDestruction(Object bean) {
return true;
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (beanName.equals("getKeycloakContainerCustomizer")) {
Object wrapRes = this.wrapOrder(bean);
return wrapRes;
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
private Object wrapOrder(Object bean) {
log.info("rewrite keycloak auto config customizer Order for next custom");
final WebServerFactoryCustomizer origin = (WebServerFactoryCustomizer) bean;
return new KeycloakContainerCustomizerWithOrder(origin);
}
}
class KeycloakContainerCustomizerWithOrder implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory>, Ordered {
private final WebServerFactoryCustomizer origin;
public KeycloakContainerCustomizerWithOrder(WebServerFactoryCustomizer origin) {
this.origin = origin;
}
@Override
public void customize(ConfigurableServletWebServerFactory factory) {
origin.customize(factory);
}
@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE - 1;
}
}
- I extra RedisIndexedSessionRepository, and set it to proxy object
@Slf4j
@Configuration
@RequiredArgsConstructor
class ContainerConfig {
private final RedisIndexedSessionRepository sessionRepository;
@Bean
public WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> getKeycloakContainerCustomizerGai() {
return configurableServletWebServerFactory -> {
if (configurableServletWebServerFactory instanceof TomcatServletWebServerFactory) {
TomcatServletWebServerFactory container = (TomcatServletWebServerFactory) configurableServletWebServerFactory;
container.getContextValves().stream().filter(ele -> ele.getClass() == KeycloakAuthenticatorValve.class).findFirst().map(ele -> (AbstractKeycloakAuthenticatorValve) ele).ifPresent(valve -> {
try {
final Field field = AbstractKeycloakAuthenticatorValve.class.getDeclaredField("userSessionManagement");
field.setAccessible(true);
final CatalinaUserSessionManagement origin = (CatalinaUserSessionManagement) field.get(valve);
field.set(valve, new CatalinaUserSessionManagementGai(origin, sessionRepository));
} catch (Exception e) {
log.error("enhence valve fail");
}
});
}
};
}
}
@Slf4j
class CatalinaUserSessionManagementGai extends CatalinaUserSessionManagement {
private final CatalinaUserSessionManagement origin;
private final RedisIndexedSessionRepository sessionRepository;
public CatalinaUserSessionManagementGai(CatalinaUserSessionManagement origin, RedisIndexedSessionRepository sessionRepository) {
this.origin = origin;
this.sessionRepository = sessionRepository;
}
public void login(Session session) {
origin.login(session);
}
public void logoutAll(Manager sessionManager) {
origin.logoutAll(sessionManager);
}
public void logoutHttpSessions(Manager sessionManager, List<String> sessionIds) {
for (String sessionId : sessionIds) {
logoutSession(sessionManager, sessionId);
}
}
protected void logoutSession(Manager manager, String httpSessionId) {
try {
final Method method = CatalinaUserSessionManagement.class.getDeclaredMethod("logoutSession", Manager.class, String.class);
method.setAccessible(true);
method.invoke(origin,manager,httpSessionId);
} catch (Exception e) {
log.error("session manager proxy invoke error");
}
// enhence part
sessionRepository.deleteById(httpSessionId);
}
protected void logoutSession(Session session) {
try {
final Method method = CatalinaUserSessionManagement.class.getDeclaredMethod("logoutSession", Session.class);
method.setAccessible(true);
method.invoke(origin,session);
} catch (Exception e) {
log.error("session manager proxy invoke error");
}
}
public void sessionEvent(SessionEvent event) {
origin.sessionEvent(event);
}
}
that work for me