Регистрация имени пользователя в KeyCloak access-log

Регистрация имени пользователя в KeyCloak access-log

В KeyCLoak 15.0 (то есть WildFly 23.0) я пытаюсь настроить access-log так, чтобы он также включал имя пользователя (или любой идентификатор пользователя), когда пользователь вошел в систему. В keycloak/standalone/configuration/standalone.xml, я настроил 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}""

Журнал корректно печатается в файле, который я настроил. Однако значение %uor %{REMOTE_USER}всегда пустое (то есть -).

Единственный способ зарегистрировать какой-либо идентификатор пользователя, который я нашел, — это зарегистрировать значение cookie сеанса с помощью %{c,KEYCLOAK_SESSION}(оно содержит realm/user-ID/secret). Это не очень хорошая идея для использования в продакшне.

Есть идеи, как зарегистрировать имя пользователя или идентификатор пользователя в журнале доступа?

Это баг KeyCLoak, что %uили %{REMOTE_USER}пусто даже когда есть активный сеанс пользователя в KeyCloak? Или в KeyCLoak можно настроить, какое значение атрибута пользователя входит REMOTE_USER?

Альтернативно, как поместить userID в какой-либо заголовок, чтобы использовать одно из следующего?

  • %{i,xxx}для входящих заголовков
  • %{o,xxx}для исходящих заголовков ответа
  • %{c,xxx}для определенного файла cookie
  • %{r,xxx}где xxx — атрибут в ServletRequest
  • %{s,xxx}где xxx — атрибут в HttpSession

Среди прочих я попробовал эти. Ни один из них не был заселен.

%{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}

решение1

Я столкнулся с похожей проблемой (мой клиент попросил меня регистрировать идентификатор клиента) и в итоге начал искать решение. Глядя на исходный код и на то, как заполняется журнал доступа, я могу сказать, что существует довольно большой разрыв между тем, где формируется журнал, и тем, где выполняется фактическая работа.

Если вы посмотрите на Keycloak, он основан на Wildfly, который использует Undertow для размещения функциональности http-сервера. Хотя запись в журнале доступа выдается после обслуживания запроса, есть несколько пробелов и абстракций, которые усложняют ситуацию.

С точки зрения программного обеспечения есть обработчик undertow, затем сервлет, затем сервлет resteasy, затем приложение keycloak и определенные ресурсы. Когда вы используете консоль пользователя или администратора Keycloak, то в большинстве мест это "тонкий" клиент, который отображается веб-браузером. И этот браузер вызывает ресурс rest.

Если вы хотите получить информацию, связанную с пользователем, то ее не будет в сеансе, потому что большая часть работы, выполняемой Kecloak, заключается в выпуске токенов от имени пользователей. Формально клиент, отправляющий запрос, действует от имени пользователя, что означает, что это не явная информация, доступная для каждого входящего запроса. Кроме того, большинство ресурсов rest по определению не имеют состояния, что означает, что они каким-то образом работают с пользователем, но не заполняют сеанс. Только одна часть, в которой вы можете рассчитывать на доступ к информации о пользователе, — это когда пользователь фактически входит в систему и делает что-то в консоли учетной записи пользователя. Кроме того, это может быть проигранная битва, поскольку ресурсы keycloak, выдающие токены, в большинстве случаев будут обрабатывать клиентские или клиентские сеансы.

К делу - я подошел к месту, где я нашел место, которое выполняет разбор формата журнала доступа. Оно основано на ExchangeAttributeидее Undertow, которая позволяет использовать собственный макрос для журнала. Этот макрос можно использовать для обхода структур памяти в поисках необходимой информации. Для меня это был client_id, который выполнял работу. Для этого я в конечном итоге реализовал FormAttribute. Мне все еще нужно найти способ подключить его, но с точки зрения модульного тестирования он уже "щелкнул", посмотрите, насколько прост код:

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;
    }
  }
}

Главное - используя атрибут формы для регистрации значения, введенного в поле "имя пользователя" формы входа, чтобы получить "кто", затем вы можете объединить это с сеансовым cookie, который будет сохранен браузером. Простым слиянием двух вышеупомянутых тузов вы можете достичь необходимого результата. Используя вышеприведенный план, вы можете реализовать свою собственную вещь и отслеживать токены и другие вещи, которые позволят вам создать свое приложение.

Я могу обновить ответ, когда найду связующую логику для правильного внедрения дополнительных атрибутов в формат журнала в undertow. EDIT: Единственный способ, который я нашел до сих пор, чтобы внедрить дополнительные атрибуты, — это скопировать их $JBOSS_HOME/modules/system/layers/base/org/wildfly/extension/undertow/main/и обновить 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>

Связанный контент