In KeyCLoak 15.0 (das ist WildFly 23.0) versuche ich, das Zugriffsprotokoll so zu konfigurieren, dass auch der Benutzername (oder eine beliebige ID des Benutzers) enthalten ist, wenn ein Benutzer angemeldet ist. In keycloak/standalone/configuration/standalone.xml
habe ich Folgendes konfiguriert:
XML:/server/profile/subsystem[@xmlns="urn:jboss:domain:undertow:12.0"]/server/host/access-log/@pattern
pattern="%h %l %u %t "%r" %s/%S %b %T %I "%{i,User-Agent}""
Das Protokoll wird korrekt in die von mir konfigurierte Datei gedruckt. Der Wert von %u
oder %{REMOTE_USER}
ist jedoch immer leer (d. h. -
).
Die einzige Möglichkeit, eine Benutzer-ID zu protokollieren, die ich gefunden habe, war das Protokollieren des Sitzungscookie-Werts mit %{c,KEYCLOAK_SESSION}
(er enthält realm/user-ID/secret
). Was in der Produktion keine gute Idee ist.
Irgendeine Idee, wie man Benutzernamen oder Benutzer-ID im Zugriffsprotokoll protokolliert?
Ist das ein KeyCLoak-Fehler %u
oder %{REMOTE_USER}
ist es leer, selbst wenn eine aktive Benutzersitzung in KeyCloak besteht? Oder ist es in KeyCLoak möglich, zu konfigurieren, welcher Benutzerattributwert eingegeben wird REMOTE_USER
?
Alternativ, wie fügt man die Benutzer-ID in einen Header ein, um eine der folgenden zu verwenden?
%{i,xxx}
für eingehende Header%{o,xxx}
für ausgehende Antwortheader%{c,xxx}
für ein bestimmtes Cookie%{r,xxx}
wobei xxx ein Attribut in der ServletRequest ist%{s,xxx}
wobei xxx ein Attribut in der HttpSession ist
Unter anderem habe ich diese hier probiert. Keine davon war belegt.
%{s,user}
%{s,userId}
%{s,client_id}
%{s,USER_ID}
%{s,USER}
%{s,org.keycloak.adapters.spi.KeycloakAccount}
%{s,KeycloakAccount}
%{s,org.keycloak.adapters.tomcat.CatalinaSessionTokenStore.SerializableKeycloakAccount}
%{s,SerializableKeycloakAccount}
%{s,org.keycloak.adapters.saml.SamlSession}
%{s,SamlSession}
%{s,org.keycloak.adapters.undertow.KeycloakUndertowAccount}
%{s,KeycloakUndertowAccount}
%{s,org.keycloak.KeycloakSecurityContext}
%{s,KeycloakSecurityContext}
%{s,io.undertow.servlet.util.SavedRequest}
%{s,SavedRequest}
%{r,tokenInfo}
%{r,KeycloakSecurityContext}
%{r,ElytronHttpFacade}
%{r,AdapterDeploymentContext}
%{r,TOKEN_STORE_NOTE}
Antwort1
Ich bin auf ein ähnliches Problem gestoßen (mein Kunde hat mich gebeten, die Kunden-ID zu protokollieren) und habe schließlich nach einer Lösung gesucht. Wenn ich mir den Quellcode anschaue und sehe, wie das Zugriffsprotokoll ausgefüllt wird, kann ich sagen, dass es eine ziemlich große Lücke zwischen dem Ort gibt, an dem das Protokoll erstellt wird, und dem Ort, an dem die eigentliche Arbeit erledigt wird.
Wenn Sie sich Keycloak ansehen, wird deutlich, dass es auf Wildfly basiert, das Undertow verwendet, um HTTP-Serverfunktionen zu hosten. Während der Zugriffsprotokolleintrag ausgegeben wird, sobald die Anfrage bearbeitet wurde, gibt es einige Lücken und Abstraktionen, die die Sache komplizieren.
Aus Softwaresicht gibt es Undertow-Handler, dann Servlet, dann Resteasy-Servlet, dann Keycloak-Anwendung und spezifische Ressourcen. Wenn Sie die Keycloak-Benutzer- oder Administratorkonsole verwenden, handelt es sich an den meisten Stellen um einen „dünnen“ Client, der von einem Webbrowser gerendert wird. Und dieser Browser ruft Rest-Ressourcen auf.
Wenn Sie benutzerbezogene Informationen abrufen möchten, werden diese häufig nicht in der Sitzung gefunden, da die meiste Arbeit von Kecloak darin besteht, Token im Namen der Benutzer auszugeben. Formal handelt der Client, der die Anfrage sendet, im Namen des Benutzers, was bedeutet, dass keine expliziten Informationen für jede eingehende Anfrage verfügbar sind. Darüber hinaus sind die meisten übrigen Ressourcen per Definition zustandslos, was bedeutet, dass sie zwar irgendwie mit dem Benutzer zusammenarbeiten, die Sitzung jedoch nicht stark bevölkern. Nur in einem Fall können Sie mit Zugriff auf Benutzerinformationen rechnen, wenn sich der Benutzer tatsächlich anmeldet und etwas in der Benutzerkontokonsole tut. Abgesehen davon könnte es ein verlorener Kampf sein, da Keycloak-Ressourcen, die Token ausgeben, in den meisten Fällen den Client oder clientbezogene Sitzungen handhaben.
Auf den Punkt gebracht – ich bin an dem Punkt angelangt, an dem ich eine Stelle gefunden habe, die das Format des Zugriffsprotokolls analysiert. Sie basiert auf der Undertow- ExchangeAttribute
Idee, die es ermöglicht, ein eigenes Makro für das Protokoll zu erstellen. Dieses Makro kann verwendet werden, um Speicherstrukturen auf der Suche nach den erforderlichen Informationen zu durchlaufen. Bei mir war es eine Client-ID, die die Arbeit erledigte. Dafür landete ich bei der Implementierung eines FormAttribute
. Ich muss noch einen Weg finden, es einzubinden, aber aus Sicht der Komponententests hat es bereits „Klick“ gemacht, sehen Sie, wie einfach der Code ist:
package org.code_house.wildfly.stuff.undertow.attributes;
// remember to create META-INF/services/io.undertow.attribute.ExchangeAttributeBuilder
// with line containing class name, ie.
// org.code_house.wildfly.stuff.undertow.attributes.FormAttribute$Builder
/**
* Expose form parameters within exchange attributes which can be logged in access log.
* Use %{F,*} to dump all params or %{F,client_id} to render selected from field.
*
* @author Łukasz Dywicki @ code-house.org
**/
public class FormAttribute implements ExchangeAttribute {
private final String paramName;
public FormAttribute(String paramName) {
this.paramName = paramName;
}
@Override
public String readAttribute(HttpServerExchange exchange) {
FormData formData = exchange.getAttachment(FormDataParser.FORM_DATA);
if ("*".equals(paramName)) {
return "" + formData;
}
return formData == null ? "" : "" + formData.get(paramName);
}
@Override
public void writeAttribute(HttpServerExchange exchange, String newValue) throws ReadOnlyAttributeException {
throw new ReadOnlyAttributeException("Form", newValue);
}
public static final class Builder implements ExchangeAttributeBuilder {
@Override
public String name() {
return "form";
}
@Override
public ExchangeAttribute build(final String token) {
if (token.startsWith("%{F,") && token.endsWith("}")) {
final String paramName = token.substring(4, token.length() - 1);
return new FormAttribute(paramName);
}
return null;
}
@Override
public int priority() {
return 0;
}
}
}
Hauptpunkt: Indem Sie das Formularattribut verwenden, um den im Feld „Benutzername“ des Anmeldeformulars eingegebenen Wert zu protokollieren, um „Wer“ zu erhalten, können Sie dies mit einem Sitzungscookie kombinieren, das vom Browser gespeichert wird. Durch die grundlegende Zusammenführung der beiden oben genannten Asse können Sie das gewünschte Ergebnis erzielen. Mithilfe der obigen Blaupause können Sie Ihre eigenen Dinge implementieren und Token und andere Dinge im Auge behalten, mit denen Sie Ihre Anwendung erstellen können.
Ich werde die Antwort möglicherweise aktualisieren, wenn ich eine Verbindungslogik finde, um zusätzliche Attribute ordnungsgemäß in das Protokollformat in Undertow einzufügen. BEARBEITEN: Bisher habe ich nur eine Möglichkeit gefunden, zusätzliche Attribute einzufügen, indem ich sie in dieses Verzeichnis kopiere $JBOSS_HOME/modules/system/layers/base/org/wildfly/extension/undertow/main/
und dort aktualisiere :module.xml
<module name="org.wildfly.extension.undertow" xmlns="urn:jboss:module:1.5">
...
<resources>
<resource-root path="wildfly-undertow-20.0.1.Final.jar"/>
<!-- put here name of jar you made -->
<resource-root path="undertow-client-request-filter-1.0.0-SNAPSHOT.jar"/>
</resources>
...
</module>