使用spring-session-jdbc的Filter后session对象在后面的CompositeFilter中为空
After using Filter of spring-session-jdbc session object is empty in later CompositeFilter
问题描述(好像是时间问题):
After using SpringSessionRepositoryFilter, session object is empty during
the processing period of OtherFilter at the begining of every request
我试过的:
- In the Controller and JSP after OtherFilter, session object is not empty and works fine
- Without using springSessionRepositoryFilter, session object is notempty and works fine in OtherFilter
配置如下:
<bean class="org.springframework.web.filter.CompositeFilter" name="springChainFilter">
<property name="filters">
<list>
<bean id="springSessionRepositoryFilter" class="org.springframework.session.web.http.SessionRepositoryFilter">
</bean>
<!--Other Later Filter -->
<bean id="otherFilter" class="xxx.xxx.OtherFilter">
</bean>
</list>
</property>
</bean>
<bean class="org.springframework.session.jdbc.config.annotation.web.http.JdbcHttpSessionConfiguration"/>
<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<constructor-arg ref="dataSource"/>
</bean>
<bean id="cookieSerializer" class="org.springframework.session.web.http.DefaultCookieSerializer">
<property name="cookieName" value="JSESSIONID" />
</bean>
OtherFilter 定义如下:
public class OtherFilter extends OncePerRequestFilter {
@Autowired
private SessionObj sessionObj;
......
}
会话对象定义如下:
@Component
@SessionScope
public class SessionObj implements Serializable {
private static final long serialVersionUID = 1L;
private String xxId;
......
}
环境版本信息:
- spring-session-jdbc-2.1.5.RELEASE
- wildfly-11.0.0.Final
- Oracle Database 18c Express Edition Release 18.0.0.0.0
这个问题的原因
- @Autowired SessionObj in OtherFilter
- @Autowired SessionObj in Controller after OtherFilter
以上两种情况,DI源都是绑定到[requestAttributes]对象的request#getsession()。
org.springframework.web.context.request.ServletRequestAttributes
protected final HttpSession getSession(boolean allowCreate) {
if (isRequestActive()) {
HttpSession session = this.request.getSession(allowCreate);
this.session = session;
return session;
}
}
在一个请求中有以下里程碑:
从a到e的序列
a. In RequestContextListner
对象 [requestAttributes] 与一个实例绑定
HttpServletRequestImpl(没有来自数据库的会话信息)
org.springframework.web.context.request.RequestContextListener
public void requestInitialized(ServletRequestEvent requestEvent) {
if (!(requestEvent.getServletRequest() instanceof HttpServletRequest)) {
throw new IllegalArgumentException(
"Request is not an HttpServletRequest: " + requestEvent.getServletRequest());
}
HttpServletRequest request = (HttpServletRequest) requestEvent.getServletRequest();
ServletRequestAttributes attributes = new ServletRequestAttributes(request);
request.setAttribute(REQUEST_ATTRIBUTES_ATTRIBUTE, attributes);
LocaleContextHolder.setLocale(request.getLocale());
RequestContextHolder.setRequestAttributes(attributes);
}
b. In SessionRepositoryFilter
- 请求被 SessionRepositoryRequestWrapper 包裹(with session
来自 DB 的信息)
但是,对象 [requestAttributes] 没有重新绑定
使用 SessionRepositoryRequestWrapper
实例
org.springframework.session.web.http.SessionRepositoryFilter
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
request.setAttribute(SESSION_REPOSITORY_ATTR, this.sessionRepository);
SessionRepositoryRequestWrapper wrappedRequest = new SessionRepositoryRequestWrapper(
request, response);
SessionRepositoryResponseWrapper wrappedResponse = new SessionRepositoryResponseWrapper(
wrappedRequest, response);
try {
filterChain.doFilter(wrappedRequest, wrappedResponse);
}
}
c. In OtherFilter
- SessionObj 是通过 [requestAttributes] 注入的
HttpServletRequestImpl 的实例(没有来自数据库的会话信息),所以
它是空的
d. In FrameworkServlet
对象 [requestAttributes] 与一个实例重新绑定
SessionRepositoryRequestWrapper(带有来自数据库的会话信息)这是
包裹在前面的b进程中
org.springframework.web.servlet.FrameworkServlet
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
initContextHolders(request, localeContext, requestAttributes);
}
private void initContextHolders(HttpServletRequest request,
@Nullable LocaleContext localeContext, @Nullable RequestAttributes requestAttributes) {
if (requestAttributes != null) {
RequestContextHolder.setRequestAttributes(requestAttributes, this.threadContextInheritable);
}
}
e. In Controller after OtherFilter
- SessionObj 是通过 [requestAttributes] 注入的
SessionRepositoryRequestWrapper 实例(会话信息来自
DB),所以它工作正常
这个问题的一个解决方案
Add a Session Retrieve Filter between springSessionRepositoryFilter
and otherFilter
<bean class="org.springframework.web.filter.CompositeFilter" name="springChainFilter">
<property name="filters">
<list>
<bean id="springSessionRepositoryFilter" class="org.springframework.session.web.http.SessionRepositoryFilter">
</bean>
<!--Session Retrieve Filter -->
<bean id="sessionRetrieveFilter" class="xxx.xxx.SessionRetrieveFilter">
<!--Other Later Filter -->
<bean id="otherFilter" class="xxx.xxx.OtherFilter">
</bean>
</list>
</property>
</bean>
In Session Retrieve Filter bind the [requestAttributes] with an
instance of SessionRepositoryRequestWrapper (do thing like
FrameworkSevlet do in future)
xxx.xxx.SessionRetrieveFilter
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
RequestAttributes wrappedAttributes = new ServletRequestAttributes(request, response);
RequestContextHolder.setRequestAttributes(wrappedAttributes);
filterChain.doFilter(request, response);
}
这个问题的另一个解决方案
Add the requestContextFilter between springSessionRepositoryFilter and
otherFilter
<bean class="org.springframework.web.filter.CompositeFilter" name="springChainFilter">
<property name="filters">
<list>
<bean id="springSessionRepositoryFilter" class="org.springframework.session.web.http.SessionRepositoryFilter">
</bean>
<!--Request Context Filter -->
<bean id="requestContextFilter" class="org.springframework.web.filter.RequestContextFilter" />
<!--Other Later Filter -->
<bean id="otherFilter" class="xxx.xxx.OtherFilter">
</bean>
</list>
</property>
</bean>
RequestContextFilter中(由spring框架应用)
- wrappedRequest 暴露给当前线程,通过两者
LocaleContextHolder 和 RequestContextHolder
- [requestAttributes] 绑定了一个实例
SessionRepositoryRequestWrapper(像 FrameworkSevlet 那样做
未来)
问题描述(好像是时间问题):
After using SpringSessionRepositoryFilter, session object is empty during the processing period of OtherFilter at the begining of every request
我试过的:
- In the Controller and JSP after OtherFilter, session object is not empty and works fine
- Without using springSessionRepositoryFilter, session object is notempty and works fine in OtherFilter
配置如下:
<bean class="org.springframework.web.filter.CompositeFilter" name="springChainFilter">
<property name="filters">
<list>
<bean id="springSessionRepositoryFilter" class="org.springframework.session.web.http.SessionRepositoryFilter">
</bean>
<!--Other Later Filter -->
<bean id="otherFilter" class="xxx.xxx.OtherFilter">
</bean>
</list>
</property>
</bean>
<bean class="org.springframework.session.jdbc.config.annotation.web.http.JdbcHttpSessionConfiguration"/>
<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<constructor-arg ref="dataSource"/>
</bean>
<bean id="cookieSerializer" class="org.springframework.session.web.http.DefaultCookieSerializer">
<property name="cookieName" value="JSESSIONID" />
</bean>
OtherFilter 定义如下:
public class OtherFilter extends OncePerRequestFilter {
@Autowired
private SessionObj sessionObj;
......
}
会话对象定义如下:
@Component
@SessionScope
public class SessionObj implements Serializable {
private static final long serialVersionUID = 1L;
private String xxId;
......
}
环境版本信息:
- spring-session-jdbc-2.1.5.RELEASE
- wildfly-11.0.0.Final
- Oracle Database 18c Express Edition Release 18.0.0.0.0
这个问题的原因
- @Autowired SessionObj in OtherFilter
- @Autowired SessionObj in Controller after OtherFilter
以上两种情况,DI源都是绑定到[requestAttributes]对象的request#getsession()。
org.springframework.web.context.request.ServletRequestAttributes
protected final HttpSession getSession(boolean allowCreate) {
if (isRequestActive()) {
HttpSession session = this.request.getSession(allowCreate);
this.session = session;
return session;
}
}
在一个请求中有以下里程碑:
从a到e的序列
a. In RequestContextListner
对象 [requestAttributes] 与一个实例绑定 HttpServletRequestImpl(没有来自数据库的会话信息)
org.springframework.web.context.request.RequestContextListener
public void requestInitialized(ServletRequestEvent requestEvent) { if (!(requestEvent.getServletRequest() instanceof HttpServletRequest)) { throw new IllegalArgumentException( "Request is not an HttpServletRequest: " + requestEvent.getServletRequest()); } HttpServletRequest request = (HttpServletRequest) requestEvent.getServletRequest(); ServletRequestAttributes attributes = new ServletRequestAttributes(request); request.setAttribute(REQUEST_ATTRIBUTES_ATTRIBUTE, attributes); LocaleContextHolder.setLocale(request.getLocale()); RequestContextHolder.setRequestAttributes(attributes); }
b. In SessionRepositoryFilter
- 请求被 SessionRepositoryRequestWrapper 包裹(with session 来自 DB 的信息)
但是,对象 [requestAttributes] 没有重新绑定 使用 SessionRepositoryRequestWrapper
实例org.springframework.session.web.http.SessionRepositoryFilter
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { request.setAttribute(SESSION_REPOSITORY_ATTR, this.sessionRepository); SessionRepositoryRequestWrapper wrappedRequest = new SessionRepositoryRequestWrapper( request, response); SessionRepositoryResponseWrapper wrappedResponse = new SessionRepositoryResponseWrapper( wrappedRequest, response); try { filterChain.doFilter(wrappedRequest, wrappedResponse); } }
c. In OtherFilter
- SessionObj 是通过 [requestAttributes] 注入的 HttpServletRequestImpl 的实例(没有来自数据库的会话信息),所以 它是空的
d. In FrameworkServlet
对象 [requestAttributes] 与一个实例重新绑定 SessionRepositoryRequestWrapper(带有来自数据库的会话信息)这是 包裹在前面的b进程中
org.springframework.web.servlet.FrameworkServlet
protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes); initContextHolders(request, localeContext, requestAttributes); } private void initContextHolders(HttpServletRequest request, @Nullable LocaleContext localeContext, @Nullable RequestAttributes requestAttributes) { if (requestAttributes != null) { RequestContextHolder.setRequestAttributes(requestAttributes, this.threadContextInheritable); } }
e. In Controller after OtherFilter
- SessionObj 是通过 [requestAttributes] 注入的 SessionRepositoryRequestWrapper 实例(会话信息来自 DB),所以它工作正常
这个问题的一个解决方案
Add a Session Retrieve Filter between springSessionRepositoryFilter and otherFilter
<bean class="org.springframework.web.filter.CompositeFilter" name="springChainFilter">
<property name="filters">
<list>
<bean id="springSessionRepositoryFilter" class="org.springframework.session.web.http.SessionRepositoryFilter">
</bean>
<!--Session Retrieve Filter -->
<bean id="sessionRetrieveFilter" class="xxx.xxx.SessionRetrieveFilter">
<!--Other Later Filter -->
<bean id="otherFilter" class="xxx.xxx.OtherFilter">
</bean>
</list>
</property>
</bean>
In Session Retrieve Filter bind the [requestAttributes] with an instance of SessionRepositoryRequestWrapper (do thing like FrameworkSevlet do in future)
xxx.xxx.SessionRetrieveFilter
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
RequestAttributes wrappedAttributes = new ServletRequestAttributes(request, response);
RequestContextHolder.setRequestAttributes(wrappedAttributes);
filterChain.doFilter(request, response);
}
这个问题的另一个解决方案
Add the requestContextFilter between springSessionRepositoryFilter and otherFilter
<bean class="org.springframework.web.filter.CompositeFilter" name="springChainFilter">
<property name="filters">
<list>
<bean id="springSessionRepositoryFilter" class="org.springframework.session.web.http.SessionRepositoryFilter">
</bean>
<!--Request Context Filter -->
<bean id="requestContextFilter" class="org.springframework.web.filter.RequestContextFilter" />
<!--Other Later Filter -->
<bean id="otherFilter" class="xxx.xxx.OtherFilter">
</bean>
</list>
</property>
</bean>
RequestContextFilter中(由spring框架应用)
- wrappedRequest 暴露给当前线程,通过两者 LocaleContextHolder 和 RequestContextHolder
- [requestAttributes] 绑定了一个实例 SessionRepositoryRequestWrapper(像 FrameworkSevlet 那样做 未来)