Спящий режим Envers : Как внедрить SecurityContext (REST) в RevisionListener?

#rest #hibernate-envers #java-ee-8 #microprofile #security-context

Вопрос:

У меня есть API REST (использует wildfly 20 с микропрофил-jwt), поэтому я хотел бы провести аудит изменений с помощью Hibernate Envers. К сожалению, я не могу получить свой основной объект : значение javax.ws.rs.core.SecurityContext равно null.

Итак, мой вопрос : как я могу ввести SecurityContext в свой ревизионный список и получить Принципала ?

 import java.security.Principal;

import javax.ws.rs.core.Context;
import javax.ws.rs.core.SecurityContext;

import org.hibernate.envers.RevisionListener;

public class CustomRevisionListener implements RevisionListener {

    @Context
    private SecurityContext context;

    @Override
    public void newRevision(Object o) {
        CustomRevEntity e = (CustomRevEntity) o;
        e.setLogin(getUser());
    }

    private String getUser() {
        if(context == null) return "anonymous no context";
        Principal principal = context.getUserPrincipal();
        return principal == null ? "anonymous" : principal.getName();
    }
}
 

Ответ №1:

Это не сработает, так как создание компонента выполняется вне контейнера. Если вы хотите использовать envers, вам понадобится способ передачи принципа от компонентов, управляемых контейнером, к компоненту RevisionListener, который создается и управляется расширением envers.

Способом может быть использование общедоступного статического ThreadLocal<Принципала> threadlocalPrincipal = новый ThreadLocal<Принципал><>();<>.

В компоненте, использующем JPA или Hibernate, должен работать контекст @Context SecurityContext. Перед вызовом любого Dao или любого другого метода, использующего режим гибернации, заполните общедоступную статическую переменную с помощью threadlocalPrincipal.set(…). Внутри прослушивателя используйте <Имя класса компонента>threadlocalPrincipal.get()<Имя класса компонента> для доступа к текущей переменной, установленной в текущем потоке.

Убедитесь, что после этого очистите переменную Threadlocal с помощью функции threadLocalPrincipal.remove() внутри блока finally, чтобы избежать утечки памяти.

Ответ №2:

Я получил ответ с форума hibernate : https://discourse.hibernate.org/t/how-to-get-username-envers-api-rest/5592

Чтобы иметь возможность ввести текст SecurityContext в RevisionListner (с @Inject ), вам нужен этот фильтр :

 @Provider
public class SecurityRequestFilter implements ContainerRequestFilter, ContainerResponseFilter {

    private static final ThreadLocal<SecurityContext> THREAD_LOCAL = new ThreadLocal<>();

    @RequestScoped
    @Produces
    public SecurityContext getSecurityContext() {
        return THREAD_LOCAL.get();
    }

    @Override
    public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext)
            throws IOException {
        THREAD_LOCAL.remove();
    }

    @Override
    public void filter(ContainerRequestContext requestContext) throws IOException {
        THREAD_LOCAL.set(requestContext.getSecurityContext());
    }
}