Quarkus Security-JWT中从外部加载groups声明遇阻塞线程问题求助
解决Quarkus Security-JWT中自定义Principal Factory的阻塞操作异常
你遇到的io.quarkus.runtime.BlockingOperationNotAllowedException是因为JWT解析流程默认在Vert.x IO线程执行,该线程禁止执行阻塞操作(如JDBC调用、同步HTTP请求)。即使添加了@Blocking注解,由于JWTCallerPrincipalFactory的执行上下文限制,线程切换逻辑可能未生效。以下是几种可行的解决方案:
方案1:确保@Blocking注解正确触发线程切换
如果坚持使用自定义JWTCallerPrincipalFactory,需确保方法在Quarkus工作线程执行,同时DAO层的阻塞操作也标记为@Blocking:
修改自定义Principal Factory
@ApplicationScoped @Alternative @Priority(1) public class JwtPrincipalFactory extends JWTCallerPrincipalFactory { private final DefaultJWTTokenParser parser = new DefaultJWTTokenParser(); private final UserRepository userRepository; // 构造注入阻塞式Panache仓库 public JwtPrincipalFactory(UserRepository userRepository) { this.userRepository = userRepository; } @Blocking // 强制方法在Quarkus工作线程执行 @Override public JWTCallerPrincipal parse(final String token, final JWTAuthContextInfo context) throws ParseException { final JwtContext jwtContext = parser.parse(token, context); final String type = ((JsonWebStructure) jwtContext.getJoseObjects().getFirst()).getHeader("typ"); // 从JWT的sub声明获取用户ID String userId = jwtContext.getJwtClaims().getSubject(); // 调用阻塞式DAO方法加载权限组 Set<String> groups = userRepository.findGroupsByUserId(userId); jwtContext.getJwtClaims().setClaim("groups", groups); return new DefaultJWTCallerPrincipal(type, jwtContext.getJwtClaims()); } }
阻塞式Panache仓库实现
@ApplicationScoped public class UserRepository extends PanacheRepository<User> { @Blocking // 标记此方法为阻塞操作,自动切换到工作线程 public Set<String> findGroupsByUserId(String userId) { return find("userId", userId) .firstResultOptional() .map(User::getGroups) .orElse(Set.of("none")); } }
方案2:使用Security Identity Augmentor(推荐)
Quarkus官方推荐用SecurityIdentityAugmentor扩展身份信息,它天然支持异步/Reactive操作,无需担心线程阻塞问题:
实现Identity Augmentor
@ApplicationScoped public class GroupsIdentityAugmentor implements SecurityIdentityAugmentor { private final UserReactiveRepository userReactiveRepository; public GroupsIdentityAugmentor(UserReactiveRepository userReactiveRepository) { this.userReactiveRepository = userReactiveRepository; } @Override public Uni<SecurityIdentity> augment(SecurityIdentity identity, AuthenticationRequestContext context) { // 从已认证的身份中获取用户ID String userId = identity.getPrincipal().getName(); // 异步加载用户权限组 return userReactiveRepository.findGroupsByUserId(userId) .map(groups -> SecurityIdentity.builder(identity) .addRoles(groups) // 将权限组添加为角色,直接用于端点权限控制 .build()) .onFailure().recoverWithItem(identity); // 加载失败时返回原始身份 } }
Reactive Panache仓库实现
@ApplicationScoped public class UserReactiveRepository extends PanacheRepositoryBase<User, Long> { public Uni<Set<String>> findGroupsByUserId(String userId) { return find("userId", userId) .firstResult() .map(User::getGroups) .onItem().ifNull().continueWith(Set.of("none")); } }
这种方式无需自定义Principal Factory,直接扩展已认证的SecurityIdentity,权限组会自动被Quarkus Security用于端点的角色校验,完全适配Reactive编程模型。
方案3:全局切换JWT认证线程(不推荐)
通过配置强制JWT认证流程在工作线程执行,但会影响全局Reactive性能,仅作为临时方案:
quarkus.smallrye-jwt.auth-mechanism=BLOCKING
内容的提问来源于stack exchange,提问作者PedroZC90
相关产品推荐
相关产品推荐

