1.46.0(7c986649)把上下文过滤器注册成 REQUEST+ASYNC 之后,#963 的上下文异常已解决。但同一场景还有另一半: Spring MVC 的异步请求在响应写完后会经历一次 Servlet ASYNC 收尾派发,`DispatcherServlet.doDispatch` 在这次派发里再次执行 `applyPreHandle`,`SaInterceptor` 于是对同一请求第二次执行注解鉴权。此时响应通常已经 committed: - SSE / `StreamingResponseBody` 长连接持续几分钟很常见(比如 AI 流式输出); - token 在流进行期间过期,或被注销/顶下线; - 收尾派发上 `StpUtil.checkLogin` 抛 `NotLoginException`; - 响应已 committed,异常无法转成错误响应,容器只能中止连接:客户端丢整条流,Nginx 记 `upstream prematurely closed connection`。 复现条件:`@SaCheckLogin` + `SseEmitter`/`StreamingResponseBody`,流的持续时间跨过 token 有效期;或者流进行中把该 token 注销。 这次重复鉴权拦不住任何东西——数据在首段鉴权通过后就已经开始下发了。建议 `SaInterceptor.preHandle` 跳过异步收尾派发: ```java if (request.getDispatcherType() == DispatcherType.ASYNC && WebAsyncUtils.getAsyncManager(request).hasConcurrentResult()) { return true; } ``` 判断条件不能放宽成 `dispatcherType != REQUEST`:`AsyncContext.dispatch("/other/path")` 这类派发到另一个资源的场景,目标 handler 的鉴权注解必须执行,一律跳过会越权。 Spring Security 在 ASYNC 派发上也执行授权过滤器,但它校验的是首段捕获、经 request attribute 恢复的 SecurityContext——等效于鉴权快照,中途注销不会阻断同一请求的收尾渲染。 `WebAsyncUtils` 在 spring-web 里,这段判断放在 webmvc 相关模块比放在 servlet 通用模块合适。
Activity
click33 commentedon Aug 18, 2026
让我歇两天 🤒