Quarkus中如何在ContainerRequestFilter中调用Repository验证JWT用户
解决Quarkus中ContainerRequestFilter调用Repository时的阻塞操作异常
问题描述
我在Quarkus项目中实现自定义JWT过滤器,需要在PostgreSQL中验证用户邮箱,但在ContainerRequestFilter里调用Repository时触发了异常。
过滤器代码
@Provider @PreMatching public class SecurityFilter implements ContainerRequestFilter { @Inject JsonWebToken jwt; @Inject UserRepository userRepository; private boolean hasJwt() { return jwt.getClaimNames() != null; } @Override public void filter(ContainerRequestContext requestContext) { if (hasJwt()) { String email = requestContext.getSecurityContext().getUserPrincipal().getName(); User user = userRepository.findByEmail(email); System.out.println(email); } else { System.out.println("No jwt"); } } }
Repository代码
@ApplicationScoped public class UserRepository implements PanacheRepository<User> { public User findByEmail(String email) { return find("email", email).firstResult(); } }
报错信息
2022-09-08 11:08:16,356 ERROR [org.jbo.res.rea.com.cor.AbstractResteasyReactiveContext] (vert.x-eventloop-thread-1) Request failed: io.quarkus.runtime.BlockingOperationNotAllowedException: You have attempted to perform a blocking operation on a IO thread. This is not allowed, as blocking the IO thread will cause major performance issues with your application. If you want to perform blocking EntityManager operations make sure you are doing it from a worker thread. at io.quarkus.hibernate.orm.runtime.session.TransactionScopedSession.checkBlocking(TransactionScopedSession.java:115) at io.quarkus.hibernate.orm.runtime.session.TransactionScopedSession.createQuery(TransactionScopedSession.java:346) at org.hibernate.engine.spi.SessionLazyDelegator.createQuery(SessionLazyDelegator.java:542) at org.hibernate.engine.spi.SessionLazyDelegator.createQuery(SessionLazyDelegator.java:65) at org.hibernate.Session_5b93bee577ae2f8d76647de04cfab36afbf52958_Synthetic_ClientProxy.createQuery(Unknown Source) at io.quarkus.hibernate.orm.panache.common.runtime.CommonPanacheQueryImpl.createBaseQuery(CommonPanacheQueryImpl.java:362) at io.quarkus.hibernate.orm.panache.common.runtime.CommonPanacheQueryImpl.createQuery(CommonPanacheQueryImpl.java:338) at io.quarkus.hibernate.orm.panache.common.runtime.CommonPanacheQueryImpl.firstResult(CommonPanacheQueryImpl.java:283) at io.quarkus.hibernate.orm.panache.runtime.PanacheQueryImpl.firstResult(PanacheQueryImpl.java:159)
解决方案
报错核心原因是IO线程上执行了阻塞操作:Quarkus的Reactive模式下,ContainerRequestFilter默认在IO线程(vert.x-eventloop-thread)运行,而数据库查询属于阻塞操作,不能在IO线程中执行,否则会严重影响应用性能。
方法一:使用@Blocking注解切换到工作线程
在filter方法上添加@Blocking注解,Quarkus会自动将该方法的执行调度到worker线程,即可安全执行阻塞的数据库操作:
@Provider @PreMatching public class SecurityFilter implements ContainerRequestFilter { @Inject JsonWebToken jwt; @Inject UserRepository userRepository; private boolean hasJwt() { return jwt.getClaimNames() != null; } @Override @Blocking // 添加此注解 public void filter(ContainerRequestContext requestContext) { if (hasJwt()) { String email = requestContext.getSecurityContext().getUserPrincipal().getName(); User user = userRepository.findByEmail(email); System.out.println(email); } else { System.out.println("No jwt"); } } }
方法二:采用Reactive异步处理方式
如果想保持Reactive风格,可将Repository查询改为异步,通过Uni包装结果,再订阅处理:
- 修改Repository,添加异步查询方法:
@ApplicationScoped public class UserRepository implements PanacheRepository<User> { public User findByEmail(String email) { return find("email", email).firstResult(); } // 新增异步查询方法 public Uni<User> findByEmailAsync(String email) { return find("email", email).firstResultAsync(); } }
- 修改过滤器,使用异步逻辑处理:
@Provider @PreMatching public class SecurityFilter implements ContainerRequestFilter { @Inject JsonWebToken jwt; @Inject UserRepository userRepository; private boolean hasJwt() { return jwt.getClaimNames() != null; } @Override public void filter(ContainerRequestContext requestContext) { if (hasJwt()) { String email = requestContext.getSecurityContext().getUserPrincipal().getName(); // 异步查询并处理结果 userRepository.findByEmailAsync(email) .subscribe().with(user -> { System.out.println(email); // 此处添加用户验证通过后的业务逻辑 }, throwable -> { // 处理查询异常,比如返回未授权响应 requestContext.abortWith(Response.status(Response.Status.UNAUTHORIZED).build()); }); } else { System.out.println("No jwt"); } } }
注意:使用异步方式需确保项目已引入Reactive相关依赖,例如
quarkus-hibernate-orm-panache-reactive。
内容的提问来源于stack exchange,提问作者Holden fcode
相关产品推荐
相关产品推荐

