spring-session和spring-security同时配置时,sccurity中获得的session不对该如何解决?

Mr_Sky 发布于 2018/03/27 16:11
阅读 1K+
收藏 1

项目中已经在使用了security

 <filter>
		<filter-name>springSessionRepositoryFilter</filter-name>
		<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
	</filter>

近期想要增加spingSession,按照网上说明添加了一个filter

<filter>
        <filter-name>springSecurityFilterChain</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    </filter>

两个filter同时配置后,spring-session是生效的,但是security中拿到的session不是同一个,改变filter顺序也没用。我认为不能这样配置两个filter,但是filter-name好像spring要求必须按照规范来写,所以没法把两个filter合并。那么该如何让spring-session和security并存,并且保证security拿到的是同一个session?

加载中
0
Mr_Sky
Mr_Sky

自己来解答一下这个问题吧:

经过几天的研究,终于搞清楚问题所在:

先贴出来我web.xml的主要配置,用于引入spring-session,spring-security,RequestContextListener

<filter>
		<filter-name>springSessionRepositoryFilter</filter-name>
		<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>

<filter>
		<filter-name>springSecurityFilterChain</filter-name>
		<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>

<listener>    
    	<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>    
 </listener>

原始现象是:通过以上配置,实现了基于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进去

<filter>
        <filter-name>springSecurityFilterChain</filter-name>
        <filter-class>com.myproject.common.dispatcher.SecurityDelegatingFilterProxy</filter-class>
    </filter>
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中处理。

以上就是整个问题的分析,请大家指正。

0
progresser
progresser

HttpServletRequest contextHolderRequest = ((ServletRequestAttributes) RequestContextHolder .getRequestAttributes()).getRequest();这一步写的太简短了,没有考虑到(ServletRequestAttributes) RequestContextHolder .getRequestAttributes()为空的情况,需要增加空判断,否则会NPE,然后new ServletRequestAttributes( (HttpServletRequest) request, (HttpServletResponse) response)这一步,后面的response参数在我的spring3.2.13版本中是没有的,只需要传request参数即可,不知道是不是版本原因导致的,目前根据楼主的方式在观察,希望有用,感谢楼主的分享。

返回顶部
顶部