You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何阻塞调用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 07:50:10