Ekstensi keycloak untuk mencegat dan memproses peristiwa di sistem

Saya melanjutkan topik rekan saya tentang Keycloak.





Siapa yang tidak membutuhkan air, tetapi hanya kode contoh, lompat ke sini .





Keycloak cukup sering digunakan sebagai solusi manajemen identitas dan akses untuk aplikasi modern dalam aplikasi perusahaan.





Keycloak ditulis dalam bahasa Java, dan pembuatnya awalnya memberikan kesempatan yang sangat nyaman untuk memperluas fungsionalitas solusi yang sudah jadi dengan apa yang disebut add-on atau secara resmi: ekstensi .





Ekstensi adalah proyek Java biasa yang terdiri dari kelas-kelas yang memperluas kelas / antarmuka Keycloak default dengan fungsionalitas tambahan yang diperlukan. Selain itu, Anda dapat memperluas fungsionalitas hampir semua kelas Keycloak dan untuk tujuan apa pun: dari perubahan minimal dalam teks pesan tentang pengguna yang salah memasukkan kata sandi, hingga mengikat Discord , sebagai penyedia Identitas .





Artikel ini akan berfokus pada perluasan pendengar acara default di 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
      
      



Seperti inilah tampilan proyek secara lengkap

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:





Jika penerapan ekstensi berhasil, plugin kami akan muncul di menu tarik-turun bidang 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