SessionRepositoryFilter未作用于转发请求,如何修改其FilterMap调度映射?
用Hazelcast存储用户会话时,正常请求会经过SessionRepositoryFilter,但请求出错转发到/error路径时,转发请求不会走这个过滤器。这会导致Tomcat自动创建一个新会话(ID格式不是Hazelcast采用的UUID),并通过Set-Cookie头让客户端切换到这个错误的会话ID。下次客户端用这个ID请求时,Hazelcast无法识别,直接返回401未授权。
问题出在Spring的AbstractFilterRegistrationBean#determineDispatcherTypes方法:SessionRepositoryFilter继承的是org.springframework.session.web.http.OncePerRequestFilter,而非Spring Web原生的org.springframework.web.filter.OncePerRequestFilter,所以该方法判定它不需要处理转发请求。这明显是个逻辑疏漏——这两个OncePerRequestFilter都应该默认覆盖所有请求调度类型。
SessionRepositoryFilter的DispatcherMapping为31 要让SessionRepositoryFilter过滤所有请求(对应dispatcherMapping值31,包含REQUEST、FORWARD、INCLUDE、ASYNC、ERROR所有调度类型),可以通过自定义FilterRegistrationBean来覆盖默认配置:
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.session.web.http.SessionRepositoryFilter; import org.springframework.boot.web.servlet.FilterRegistrationBean; import javax.servlet.DispatcherType; @Configuration public class SessionFilterConfig { @Bean public FilterRegistrationBean<SessionRepositoryFilter<?>> customizeSessionRepositoryFilter( SessionRepositoryFilter<?> sessionRepositoryFilter) { FilterRegistrationBean<SessionRepositoryFilter<?>> registrationBean = new FilterRegistrationBean<>(sessionRepositoryFilter); // 覆盖默认的DispatcherTypes,包含所有类型(对应数值31) registrationBean.setDispatcherTypes(DispatcherType.REQUEST, DispatcherType.FORWARD, DispatcherType.INCLUDE, DispatcherType.ASYNC, DispatcherType.ERROR); // 保留原过滤器的执行顺序,避免打乱现有过滤链 registrationBean.setOrder(sessionRepositoryFilter.getOrder()); return registrationBean; } }
如果你想直接用数值对应的逻辑设置,也可以用更简洁的写法(效果完全一致):
// 直接设置所有DispatcherType,对应数值31 registrationBean.setDispatcherTypes(DispatcherType.values());
配置完成后,触发一次错误请求(比如故意返回500),检查:
- 转发到
/error的请求是否经过SessionRepositoryFilter(可通过日志或断点验证) - 响应头的Set-Cookie是否还是Hazelcast生成的UUID格式会话ID
- 后续用该会话ID请求时,是否不再返回401未授权
内容的提问来源于stack exchange,提问作者whistling_marmot

