我有一个 REST API(使用带有 microprofile-jwt 的 wildfly 20),因此我想使用 Hibernate Envers 审核更改。不幸的是,我无法获取我的主要对象:
javax.ws.rs.core.SecurityContext
为空。
所以我的问题是:如何在 RevisionListener 中注入 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();
}
}
这是行不通的,因为 bean 的创建是在容器外部完成的。如果您想使用 envers,则需要一种方法将容器管理的 bean 的原理传达给 envers 扩展创建和管理的 RevisionListener-bean。
一种方法可能是使用 public static ThreadLocal
在使用 JPA 或 Hibernate 的 bean 中,@Context SecurityContext 上下文应该可以工作。在调用任何 Dao 或任何其他 Hibernate-Using 方法之前,使用 threadlocalPrincipal.set(...) 填充公共静态变量。在监听器内部使用
确保之后在finally块中使用threadLocalPrincipal.remove()清除Threadlocal变量,以避免内存泄漏。
我从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());
}
}
此解决方案适用于 Quarkus 3.6.5+ 吗?对我来说,SecurityContext 在这些版本中返回 null。仅在 Quarkus 3.6.4 或更低版本 中才有效。