如何阻塞调用Mono?同步获取ReactiveSecurityContextHolder中的JWT令牌
}
执行时触发错误:
block()/blockFirst()/blockLast() are blocking, which is not supported in thread parallel-2
原因是Reactor禁止在响应式线程中使用阻塞调用,即便旧版本能运行。 我尝试了两种基于官方文档的方案,均失败: ## 尝试1 ```java Mono<Object> mono = ReactiveSecurityContextHolder.getContext() .map(securityContext -> securityContext.getAuthentication().getPrincipal()); Mono<Object> objectMono = mono.subscribeOn(Schedulers.boundedElastic()); Object result = objectMono.block();
尝试2
Mono<Object> mono = ReactiveSecurityContextHolder.getContext() .map(securityContext -> securityContext.getAuthentication().getPrincipal()); mono.subscribeOn(Schedulers.boundedElastic()); Object result = mono.block();
两种尝试都报相同错误。
补充测试
以下简单代码也触发同样错误:
A)
Mono.just("qwerty") .subscribeOn(Schedulers.boundedElastic()) .publishOn(Schedulers.boundedElastic()) .block()
B)
Mono<String> customMono = Mono.just("qwerty"); Mono<String> blockedMono = customMono .subscribeOn(Schedulers.boundedElastic()) .publishOn(Schedulers.boundedElastic()); System.out.println("blockedMono.block(): " + blockedMono.block());
C)
Mono<String> customMono = Mono.just("qwerty"); Mono<String> blockedMono = Mono.just(0) .subscribeOn(Schedulers.boundedElastic()) .publishOn(Schedulers.boundedElastic()) .then(customMono); System.out.println("blockedMono.block(): " + blockedMono.block());
解决方案
核心问题是你在Reactor的非阻塞线程(parallel线程池)中调用了block(),即便用subscribeOn或publishOn切换线程池,只要发起block()的线程本身是被Reactor标记为禁止阻塞的线程,就会触发错误。
1. 遵循响应式编程模型(推荐)
彻底放弃阻塞调用,将接口改造为返回响应式类型,通过链式调用处理逻辑:
@GetMapping(..) public Mono<Void> someEndpoint(...) { return ReactiveSecurityContextHolder.getContext() .map(securityContext -> securityContext.getAuthentication().getPrincipal()) .flatMap(principal -> { // 处理获取到的Principal,比如调用Minio的响应式API交换STS令牌 return minioReactiveClient.exchangeStsToken(principal.toString()) .then(Mono.empty()); // 逻辑完成后返回空Mono }); }
2. 临时阻塞方案(不推荐)
如果依赖的第三方库只有阻塞API,必须阻塞调用,需确保block()的发起线程不是Reactor的非阻塞线程,可将逻辑提交到boundedElastic线程池执行:
@GetMapping(..) public void someEndpoint(...) { Mono<Object> principalMono = ReactiveSecurityContextHolder.getContext() .map(securityContext -> securityContext.getAuthentication().getPrincipal()); // 将阻塞逻辑提交到boundedElastic线程池执行 Schedulers.boundedElastic().schedule(() -> { try { Object principal = principalMono.block(); // 处理Principal的阻塞逻辑 } catch (Exception e) { // 处理异常 } }); }
此方案会破坏响应式非阻塞特性,仅作为临时妥协,不建议生产环境使用。
3. 直接获取原始JWT令牌
若目标是获取原始JWT字符串,可通过两种方式:
从请求头提取
@GetMapping(..) public Mono<Void> someEndpoint(ServerHttpRequest request) { String authHeader = request.getHeaders().getFirst(HttpHeaders.AUTHORIZATION); if (authHeader != null && authHeader.startsWith("Bearer ")) { String jwtToken = authHeader.substring(7); // 使用JWT令牌交换Minio STS令牌 return exchangeStsToken(jwtToken).then(Mono.empty()); } return Mono.error(new UnauthorizedException("Missing JWT token")); }
从SecurityContext获取
@GetMapping(..) public Mono<Void> someEndpoint(...) { return ReactiveSecurityContextHolder.getContext() .map(SecurityContext::getAuthentication) .cast(JwtAuthenticationToken.class) .map(JwtAuthenticationToken::getToken) .map(Jwt::getTokenValue) .flatMap(jwtToken -> { // 交换Minio STS令牌 return exchangeStsToken(jwtToken).then(Mono.empty()); }); }
内容的提问来源于stack exchange,提问作者gstackoverflow
相关产品推荐
相关产品推荐

