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

Webflux中ErrorWebExceptionHandler无法获取登录Principal的问题解决

问题分析与解决方案

为什么仅在控制器/服务中能获取Principal?

Webflux的请求处理基于反应式流,ReactiveSecurityContextHolder的上下文是通过**Mono/Flux的上下文(Context)**传递的:

  • 控制器、服务处于正常请求处理流中:从请求进入到业务调用的全链路,所有反应式操作共享同一个上下文链,SecurityContext会自动传递,因此能正常拿到Principal。
  • 全局错误处理器的默认执行逻辑:请求抛出异常后,默认错误处理分支可能脱离原始请求的上下文链。如果错误处理器未正确绑定到原始上下文,ReactiveSecurityContextHolder就无法获取认证信息,返回空上下文。

另外,request.getPrincipal()默认返回Mono.empty(),是因为Webflux Security不会将Principal直接存储在ServerHttpRequest中,而是通过ReactiveSecurityContextHolder的上下文传递,直接调用该方法自然拿不到信息。

实现预期效果的步骤

1. 以反应式方式绑定上下文到错误处理逻辑

不要在错误处理器中同步调用获取Principal的方法,要将获取SecurityContext的操作与错误处理逻辑放在同一个反应式流中,确保上下文传递不中断。

修改GlobalErrorWebExceptionHandler的handle方法,示例代码如下:

import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.security.core.context.ReactiveSecurityContextHolder;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebExceptionHandler;
import reactor.core.publisher.Mono;

// 优先级高于默认ErrorWebExceptionHandler(默认@Order(-1))
@Order(-2)
@Component
public class GlobalErrorWebExceptionHandler implements WebExceptionHandler {

    @Override
    public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
        return ReactiveSecurityContextHolder.getContext()
                // 提取用户名,无认证则返回"N/A"
                .map(SecurityContext::getAuthentication)
                .map(auth -> auth != null ? auth.getName() : "N/A")
                .defaultIfEmpty("N/A")
                .flatMap(username -> {
                    // 构建包含用户名的错误响应
                    ServerHttpResponse response = exchange.getResponse();
                    response.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
                    response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
                    
                    String errorJson = String.format(
                            "{\"error_type\":\"%s\",\"username\":\"%s\",\"error_msg\":\"%s\"}",
                            ex.getClass().getSimpleName(),
                            username,
                            ex.getMessage()
                    );
                    
                    return response.writeWith(
                            Mono.just(response.bufferFactory().wrap(errorJson.getBytes()))
                    );
                })
                // 处理上下文获取失败的兜底逻辑
                .onErrorResume(e -> {
                    ServerHttpResponse response = exchange.getResponse();
                    response.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
                    return response.setComplete();
                });
    }
}

2. 确保错误处理器优先级正确

通过@Order注解设置比默认错误处理器更高的优先级(数值更小,比如@Order(-2)),让你的处理器先执行,保证能捕获到原始请求的上下文。

3. 禁止阻塞操作

错误处理器中绝对不能使用block()这类阻塞式调用,否则会破坏反应式流的上下文传递,导致SecurityContext丢失,所有操作必须保持反应式链式调用。

内容的提问来源于stack exchange,提问作者CptWasp

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 19:12:40