[JAVA] Multi-locataire avec Keycloak

introduction

J'ai essayé de réaliser des multi-locataires en utilisant Keycloak pour utiliser des micro services dans plusieurs projets.

Ce que j'ai essayé

En regardant le Original Manual, il semble que cela puisse être fait en utilisant la méthode resolver () de l'interface KeycloakConfigResolver. Cependant, j'aimerais que vous vous absteniez de préparer les configurations Keycloak pour plusieurs projets dans des fichiers Json, et une enquête plus approfondie a révélé que les configurations Keycloak peuvent être générées dynamiquement avec AdapterConfig comme argument. Étant donné que auth_server_url et resource sont communs, vous pouvez réaliser le multi-tenant en définissant dans application.yml et en définissant l'ID de projet intégré dans l'en-tête de la demande sur le nom de domaine de Keycloak. Cependant, je pensais que générer une configuration Keycloak pour chaque requête serait un problème de performances, j'ai donc décidé de la mettre en cache pendant un temps limité avec Guava's Cache Builder. ..

Code source

RequestBasedKeycloakConfigResolver.java


import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import org.keycloak.adapters.KeycloakConfigResolver;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.KeycloakDeploymentBuilder;
import org.keycloak.adapters.OIDCHttpFacade;
import org.keycloak.representations.adapters.config.AdapterConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;

public class RequestBasedKeycloakConfigResolver implements KeycloakConfigResolver {
    
    private final Logger logger = LoggerFactory.getLogger(getClass());
    
    private final LoadingCache<String, KeycloakDeployment> keycloakDeploymentCache;
    
    public RequestBasedKeycloakConfigResolver() {
        keycloakDeploymentCache = CacheBuilder
                .newBuilder()
                .refreshAfterWrite(10, TimeUnit.MINUTES)
                .build(
                        new CacheLoader<String, KeycloakDeployment>(){
                            @Override
                            public KeycloakDeployment load(String realm) throws Exception {
                                return loadKeycloakDeployment(realm);
                            }
                        }
                );
            
    }
    
    @Value("${keycloak.auth-server-url}")
    private String authServerUrl;
    
    @Value("${keycloak.resource}")
    private String resource;
    
    @Override
    public KeycloakDeployment resolve(OIDCHttpFacade.Request request) {
        String realm = request.getHeader("project-id");
        try {
            return keycloakDeploymentCache.get(realm);
        } catch (ExecutionException ex) {
            logger.error(ex.getMessage());
            return loadKeycloakDeployment(realm);
        }
    }
    
    private KeycloakDeployment loadKeycloakDeployment(String realm) {
        AdapterConfig cfg = new AdapterConfig();
        cfg.setRealm(realm);
        cfg.setAuthServerUrl(authServerUrl);
        cfg.setResource(resource);
        return KeycloakDeploymentBuilder.build(cfg);
    }
}

Résumé

Puisque Keycloak peut définir la méthode d'authentification, etc. pour chaque domaine, il était possible de prendre en charge plusieurs projets en incorporant le nom de domaine dans l'en-tête de la requête et en changeant le domaine pour chaque requête avec KeycloakConfigResolver. Changer les paramètres de sécurité de Keycloak pour chaque demande était un problème de performances, j'ai donc décidé de le conserver dans un cache limité dans le temps pendant 10 minutes pour le moment.

Matériel de référence

Recommended Posts

Multi-locataire avec Keycloak
Multi-projet avec Keycloak
Configuration de Keycloak