自己来解答一下这个问题吧:
经过几天的研究,终于搞清楚问题所在:
先贴出来我web.xml的主要配置,用于引入spring-session,spring-security,RequestContextListener
springSessionRepositoryFilter
org.springframework.web.filter.DelegatingFilterProxy
springSecurityFilterChain
org.springframework.web.filter.DelegatingFilterProxy
org.springframework.web.context.request.RequestContextListener
原始现象是:通过以上配置,实现了基于spring-session的集群管理所有session(redis session的配置省略不贴),这时在正常访问某个url时,在controller中,从RequestContextHolder(springmvc提供)可以获取正确的session,也就是被spring-session包装过的session。但是在点击登录时,在security的自定义filter中从RequestContextHolder拿到的是原始的ServletRequest。进一步debug代码发现,登录的url(/account/auth)是经过security的配置的,被spring-security拦截了,这个拦截是由一个security拦截链上插入的一个自定义的Filter>>>>MyprojectLoginFilter所拦截,因为这时请求还未走进DispatcherServlet父类的FrameworkServlet的processRequest,所以RequestContextHolder中的request没有被包装成spring-session的request。那么这时的RequestContextHolder的request是哪里来的呢
?其实是在RequestContextListener接收到的,这个listener的代码如下
public void requestInitialized(ServletRequestEvent requestEvent) {
if (!(requestEvent.getServletRequest() instanceof HttpServletRequest)) {
throw new IllegalArgumentException((new StringBuilder())
.append("Request is not an HttpServletRequest: ")
.append(requestEvent.getServletRequest()).toString());
} else {
HttpServletRequest request = (HttpServletRequest) requestEvent
.getServletRequest();
ServletRequestAttributes attributes = new ServletRequestAttributes(
request);
request.setAttribute(REQUEST_ATTRIBUTES_ATTRIBUTE, attributes);
LocaleContextHolder.setLocale(request.getLocale());
RequestContextHolder.setRequestAttributes(attributes);
return;
}
}
可以看到监听在接收到requestEvent
后,new了一个ServletRequestAttributes并set到RequestContextHolder中。在没有到达springmvc的DispatcherServlet前,RequestContextHolder中都是原始的HttpServletRequest,而自定义的security的filter是在DispatcherServlet之前执行的,所以filter获得的request是原始的。知道这样的原因后就知道怎么改造了,我暂时这样做:web.xml中security的filter自己来实现,在super.invokeDelegate之前先判断RequestContextHolder的request是不是包装后的session,不是的话自己set进去
springSecurityFilterChain
com.myproject.common.dispatcher.SecurityDelegatingFilterProxy
public class SecurityDelegatingFilterProxy extends DelegatingFilterProxy {
/*
* (non-Javadoc)
*
* @see
* org.springframework.web.filter.DelegatingFilterProxy#invokeDelegate(javax
* .servlet.Filter, javax.servlet.ServletRequest,
* javax.servlet.ServletResponse, javax.servlet.FilterChain)
*/
@Override
protected void invokeDelegate(Filter delegate, ServletRequest request,
ServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
if (request instanceof HttpServletRequest
&& response instanceof HttpServletResponse) {
HttpServletRequest contextHolderRequest = ((ServletRequestAttributes) RequestContextHolder
.getRequestAttributes()).getRequest();
if (request != contextHolderRequest) {
RequestContextHolder.setRequestAttributes(
new ServletRequestAttributes(
(HttpServletRequest) request,
(HttpServletResponse) response), true);
}
}
super.invokeDelegate(delegate, request, response, filterChain);
}
}
为什么不在spring-session的filter中进行替换?因为这个filter中拿到的始终是原始request,只有经过它doFilter后,reqeust才是包装后的。所以没法在这个filter中处理。
以上就是整个问题的分析,请大家指正。