Spring Security 3.2.3中HttpSessionSecurityContextRepository找不到SecurityContext问题
首先咱们拆解下你遇到的核心问题:自定义过滤器的执行顺序错误,导致SecurityContext被SecurityContextPersistenceFilter意外覆盖,最终触发@Secured方法的403拦截。
为什么会出现这个问题?
Spring Security的过滤器链有严格的执行顺序,SecurityContextPersistenceFilter是整个链的核心基础过滤器之一,它的核心职责是:
- 请求开始时,从HttpSession中加载
SecurityContext到SecurityContextHolder - 请求结束时,将
SecurityContextHolder中的上下文持久化回HttpSession
而你把自定义的myJ2eePreAuthenticatedProcessingFilter设置为before="FIRST",也就是在所有过滤器之前执行,这就导致了以下冲突流程:
- 自定义过滤器先执行:创建有效
SecurityContext并放到SecurityContextHolder,但此时还没将上下文存入HttpSession SecurityContextPersistenceFilter随后执行:- 从HttpSession加载上下文(此时session里还没有,所以返回空上下文)
- 调用
SecurityContextHolder.setContext(contextBefore),直接把你刚设置的有效上下文覆盖成空的!
- 处理
@Secured注解的MethodSecurityInterceptor检查SecurityContextHolder时,发现上下文为空,就返回403禁止访问。
至于/intern/**路径下正常,是因为该路径的<http>配置里auto-config="false",且没有显式添加SecurityContextPersistenceFilter,不存在覆盖上下文的步骤,自定义过滤器设置的上下文能一直保留到后续处理。
你调试时能通过SecurityContextHolder.getContext()拿到上下文,大概率是在SecurityContextPersistenceFilter执行前的断点,或者请求结束阶段的saveContext步骤(此时上下文可能已被其他逻辑恢复,但权限拦截已经执行完毕)。
怎么解决?
调整过滤器执行顺序
你需要把自定义的J2eePreAuthenticatedProcessingFilter放在SecurityContextPersistenceFilter之后执行,Spring Security提供了内置的过滤器顺序常量,直接用after="SECURITY_CONTEXT_FILTER"指定即可:
<custom-filter ref="myJ2eePreAuthenticatedProcessingFilter" after="SECURITY_CONTEXT_FILTER" />
调整后的执行流程就会变成:
SecurityContextPersistenceFilter先加载session中的上下文(如果存在)到Holder- 自定义过滤器执行:如果Holder中没有有效上下文,就创建并设置进去
- 请求结束时,
SecurityContextPersistenceFilter自动把Holder中的上下文存回session,无需手动操作
验证全局配置
确保所有需要用到该自定义过滤器的<http>块都正确配置了过滤器顺序,尤其是包含@Secured方法的控制器对应的路径配置。
关于临时解决方案的说明
你手动把上下文存入HttpSession的SPRING_SECURITY_CONTEXT键,相当于提前完成了SecurityContextPersistenceFilter的saveContext步骤。当SecurityContextPersistenceFilter执行loadContext时,能从session中拿到你手动存入的有效上下文,不会覆盖Holder里的内容,所以权限拦截能正常识别。
内容的提问来源于stack exchange,提问作者Ricardo Vila

