#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());
}
}