Extensión de capa clave para interceptar y procesar eventos en el sistema

Continúo con el tema de mi colega sobre Keycloak.





Quién no necesita agua, sino solo un código de muestra, salte aquí .





Keycloak se utiliza con bastante frecuencia como una solución de gestión de identidad y acceso para aplicaciones modernas dentro de aplicaciones empresariales.





Keycloak está escrito en el lenguaje Java, y los creadores inicialmente presentaron una oportunidad muy conveniente para expandir la funcionalidad de la solución lista para usar con los llamados complementos u oficialmente: extensiones .





Una extensión es un proyecto Java normal que consta de clases que amplían las clases / interfaces de Keycloak predeterminadas con funcionalidad adicional requerida. Además, puede expandir la funcionalidad de casi cualquier clase de Keycloak y para cualquier propósito: desde un cambio mínimo en el texto de un mensaje sobre un usuario incorrecto que ingresó una contraseña, hasta vincular un Discord , como proveedor de identidad .





Este artículo se centrará en ampliar el detector de eventos predeterminado en Keycloak.





: .





Java . Maven. pom.xml :





<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <artifactId>event-listener-keycloak-extension</artifactId>

    <parent>
        <groupId>org.keycloak</groupId>
        <artifactId>keycloak-parent</artifactId>
        <version>12.0.4</version>
    </parent>

    <properties>
        <keycloak.version>12.0.4</keycloak.version>
        <lombok.version>1.18.20</lombok.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.keycloak</groupId>
            <artifactId>keycloak-core</artifactId>
            <version>${keycloak.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.keycloak</groupId>
            <artifactId>keycloak-server-spi</artifactId>
            <version>${keycloak.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.keycloak</groupId>
            <artifactId>keycloak-server-spi-private</artifactId>
            <version>${keycloak.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.keycloak</groupId>
            <artifactId>keycloak-services</artifactId>
            <version>${keycloak.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.keycloak</groupId>
            <artifactId>keycloak-saml-core-public</artifactId>
            <version>${keycloak.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.jboss.logging</groupId>
            <artifactId>jboss-logging</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.jboss.spec.javax.ws.rs</groupId>
            <artifactId>jboss-jaxrs-api_2.1_spec</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>${lombok.version}</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>

    <build>
        <finalName>event-listener-keycloak-extension</finalName>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>11</source>
                    <target>11</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>
      
      



Lombok. .





, :





  1. EventListenerProvider



    . . .





  2. EventListenerProviderFactory



    . EventListenerProvider



    . EventListenerProvider



    , - . Keycloak.





EventListenerProvider

EventListenerProvider



:





@Slf4j
@NoArgsConstructor
public class CustomEventListenerProvider implements EventListenerProvider {

    @Override
    public void onEvent(Event event) {
      log.info("Caught event {}", EventUtils.toString(event));
    }

    @Override
    public void onEvent(AdminEvent adminEvent, boolean b) {
        log.info("Caught admin event {}", EventUtils.toString(adminEvent));
		}

    @Override
    public void close() {

    }
}
      
      



:





  1. onEvent



    , , , , . : , id , IP . .





  2. onAdminEvent



    "" , : Keycloak.





  3. close



    , .





EventUtils



, .





EventListenerProviderFactory

EventListenerProviderFactory



:





public class CustomEventListenerProviderFactory implements EventListenerProviderFactory {

    private static final String LISTENER_ID = "event-listener-extension";

    @Override
    public EventListenerProvider create(KeycloakSession session) {
        return new CustomEventListenerProvider();
    }

    @Override
    public void init(Config.Scope scope) {

    }

    @Override
    public void postInit(KeycloakSessionFactory keycloakSessionFactory) {

    }

    @Override
    public void close() {

    }

    @Override
    public String getId() {
        return LISTENER_ID;
    }

}
      
      



, , :





  1. create



    CustomEventListenerProvider



    . . CustomEventListenerProviderFactory



    Keycloak.





  2. init



    .





  3. postInit



    .





  4. close



    Keycloak .





  5. getId



    .





. , , , Event



AdminEvent



:





@UtilityClass
public class EventUtils {

    public static String toString(AdminEvent adminEvent) {
        StringBuilder sb = new StringBuilder();

        sb.append("operationType="); sb.append(adminEvent.getOperationType());
        sb.append(", realmId="); sb.append(adminEvent.getAuthDetails().getRealmId());
        sb.append(", clientId="); sb.append(adminEvent.getAuthDetails().getClientId());
        sb.append(", userId="); sb.append(adminEvent.getAuthDetails().getUserId());
        sb.append(", ipAddress="); sb.append(adminEvent.getAuthDetails().getIpAddress());
        sb.append(", resourcePath="); sb.append(adminEvent.getResourcePath());

        if (adminEvent.getError() != null) {
            sb.append(", error="); sb.append(adminEvent.getError());
        }
        return sb.toString();
    }

    public static String toString(Event event) {
        StringBuilder sb = new StringBuilder();

        sb.append("type="); sb.append(event.getType());
        sb.append(", realmId="); sb.append(event.getRealmId());
        sb.append(", clientId="); sb.append(event.getClientId());
        sb.append(", userId="); sb.append(event.getUserId());
        sb.append(", ipAddress="); sb.append(event.getIpAddress());

        if (event.getError() != null) {
            sb.append(", error="); sb.append(event.getError());
        }

        if (event.getDetails() != null) {
            for (Map.Entry<String, String> e : event.getDetails().entrySet()) {
                sb.append(", "); sb.append(e.getKey());
                if (e.getValue() == null || e.getValue().indexOf(' ') == -1) {
                    sb.append("="); sb.append(e.getValue());
                } else {
                    sb.append("='"); sb.append(e.getValue()); sb.append("'");
                }
            }
        }
        return sb.toString();
    }
}

      
      



CustomEventListenerProviderFactory



Keycloak.





org.keycloak.events.EventListenerProviderFactory



src/main/resources/META-INF/services/



. .





:





ru.event.listener.extension.factory.CustomEventListenerProviderFactory
      
      



. Keycloak . .





JAR . Maven, , target



JAR . , -sources



. keycloak-logging-plugin.jar



. :





mvn clean package
      
      



Así es como se ve el proyecto completo

Keycloak , . ,   . , keycloak.





JAR keycloak-logging-plugin.jar



Keycloak <_KEYCLOAK>/standalone/deployments/



, Keycloak . , keycloak hot swap " ". JAR , keycloak .





, .





, Keycloak :





19:37:58,203 INFO [org.jboss.as.server.deployment] (MSC service thread 1-1) WFLYSRV0027: Starting deployment of "event-listener-keycloak-extension.jar" (runtime-name: "event-listener-keycloak-extension.jar")
19:37:58,322 INFO [org.keycloak.subsystem.server.extension.KeycloakProviderDeploymentProcessor] (MSC service thread 1-7) Deploying Keycloak provider: event-listener-keycloak-extension.jar
19:37:58,334 WARN [org.keycloak.services] (MSC service thread 1-7) KC-SERVICES0047: event-listener-extension (ru.event.listener.extension.factory.CustomEventListenerProviderFactory) is implementing the internal SPI eventsListener. This SPI is internal and may change without notice
19:37:58,366 INFO [org.jboss.as.server] (DeploymentScanner-threads - 1) WFLYSRV0010: Deployed "event-listener-keycloak-extension.jar" (runtime-name : "event-listener-keycloak-extension.jar")

      
      



JAR .deployed



.





. Keycloak. Events → Config:





Si la implementación de la extensión fue exitosa, nuestro complemento aparecerá en el menú desplegable del campo Event Listeners.
, Event Listeners .

Save.





- . :





20:02:14,474 INFO  [ru.event.listener.extension.CustomEventListenerProvider] (default task-11) Caught event type=LOGIN, realmId=master, clientId=account-console, userId=8cbc9aec-0c5f-45e0-b614-baf9e96c2278, ipAddress=127.0.0.1, auth_method=openid-connect, auth_type=code, redirect_uri=http://localhost:8080/auth/realms/master/account/#/, consent=no_consent_required, code_id=007a3edc-4541-4648-b1e6-44c30349c001, username=test
      
      



:





20:03:13,143 INFO  [ru.event.listener.extension.CustomEventListenerProvider] (default task-11) Caught event type=LOGOUT, realmId=master, clientId=null, userId=8cbc9aec-0c5f-45e0-b614-baf9e96c2278, ipAddress=127.0.0.1, redirect_uri=http://localhost:8080/auth/realms/master/account/#/
      
      



:





20:03:42,204 WARN  [org.keycloak.events] (default task-11) type=LOGIN_ERROR, realmId=master, clientId=account-console, userId=8cbc9aec-0c5f-45e0-b614-baf9e96c2278, ipAddress=127.0.0.1, error=invalid_user_credentials, auth_method=openid-connect, auth_type=code, redirect_uri=http://localhost:8080/auth/realms/master/account/#/, code_id=f0d48657-3673-4875-bb72-a7f1d89b6d31, username=test, authSessionParentId=f0d48657-3673-4875-bb72-a7f1d89b6d31, authSessionTabId=h6V1w1C3Zjk
      
      



(AdminEvent



):





20:05:05,045 INFO  [ru.event.listener.extension.CustomEventListenerProvider] (default task-20) Caught admin event operationType=ACTION, realmId=master, clientId=cff15a39-3a5d-49c6-baf1-1c8d9dee1ce6, userId=a64026c4-689f-4213-8229-b8ac471150ea, ipAddress=127.0.0.1, resourcePath=users/8cbc9aec-0c5f-45e0-b614-baf9e96c2278/reset-password
      
      



Keycloak, , . , REST . .





P.S.

, Keycloak , , (Keycloak , brute force detection).





, , - , , Keycloak . , Keycloak, . , - , BruteForceProtector



, , .





, , , , .








All Articles