engma
engma

Reputation: 1959

Spring Session with Spring Security creates two sessions

I have a Spring 4.1 web app, I use spring-security and spring session, spring session I want to use with websockets to keep the session without timeout as long as the websocket is working.

It all works fine but what I find is that I obviously have two sessions running, one for spring security and one for spring session.

This is my session.xml:

   <bean id="jedisConnFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
          <property name="hostName" value="localhost"/>
          <property name="port" value="6379" />
   </bean>

   <bean class="org.springframework.session.data.redis.RedisOperationsSessionRepository" name="sessionRepository" >
          <constructor-arg name="redisConnectionFactory" ref="jedisConnFactory" />
          <!--<property name="defaultMaxInactiveInterval" value="60" />-->
   </bean>

   <bean class="org.springframework.session.web.http.HeaderHttpSessionStrategy" name="sessionStrategy" />

   <bean name="springSessionRepositoryFilter" class="org.springframework.session.web.http.SessionRepositoryFilter">
          <constructor-arg name="sessionRepository" ref="sessionRepository" />
   </bean>

   <bean class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration" >
          <property name="httpSessionStrategy" ref="sessionStrategy"/>
   </bean>

And here is my security.xml:

<!-- Security -->
<bean id="userDetailsService" class="com.fg.ts.base.security.userdetails.CustomUserDetailsService" />

<bean id="passwordEncoder" class="org.springframework.security.crypto.password.StandardPasswordEncoder">
    <constructor-arg value="${encoder.password}" />
</bean>

<sec:authentication-manager id="authenticationManager">
    <sec:authentication-provider user-service-ref="userDetailsService">
        <sec:password-encoder ref="passwordEncoder" />
    </sec:authentication-provider>
</sec:authentication-manager>

<sec:global-method-security pre-post-annotations="enabled" secured-annotations="disabled"
                            jsr250-annotations="enabled" authentication-manager-ref="authenticationManager" />

<!-- Exclude the resources+error+auth pages from security filter -->
<sec:http pattern="/javax.faces.resource/**" security="none" />
<sec:http pattern="/images/**" security="none" />
<sec:http pattern="/css/**" security="none" />
<sec:http pattern="/ico/**" security="none" />
<sec:http pattern="/lib/**" security="none" />
<sec:http pattern="/js/**" security="none" />
<sec:http pattern="/auth/**" security="none" />
<sec:http pattern="/error/**" security="none" />
<sec:http pattern="/api/concordancer/search/**" security="none" />
<sec:http pattern="/concordancer/search" security="none"/>
<sec:http pattern="/assets/cdSearch.xhtml" security="none"/>
<sec:http pattern="/api/tmGroup/**" security="none"/>
<sec:http pattern="/api/reports/download/**" security="none"/>
<sec:http pattern="/api/socket/tasks/info/**" security="none"/>

<!-- Security Configuration for Faces Pages -->
<!-- Start Customization for Faces -->
<!-- ***************************** -->
<!-- Failure Login Handler -->
<bean id="authenticationFailureHandler" class="com.fg.ts.web.util.security.LoginFailureHandler">
    <property name="defaultFailureUrl" value="/auth/login" />
</bean>

<!-- Faces Redirect -->
<bean id="facesRedirectStrategy" class="com.fg.ts.web.util.security.FacesRedirectStrategy">
    <property name="invalidSessionUrl" value="/auth/login/sessionExpired" />
</bean>
<!-- Shared Objects -->
<bean id="securityContextRepository" class="org.springframework.security.web.context.HttpSessionSecurityContextRepository" />
<bean id="sessionRegistry" class="org.springframework.security.core.session.SessionRegistryImpl" />


<!-- Session Authentication Strategy -->
<bean id="sessionFixationAuthenticationStrategy" class="org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy" />
<bean id="registerSessionStrategy" class="org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy">
    <constructor-arg name="sessionRegistry" ref="sessionRegistry" />
</bean>
<bean id="sessionAuthenticationStrategy" class="org.springframework.security.web.authentication.session.CompositeSessionAuthenticationStrategy">
    <constructor-arg name="delegateStrategies">
        <list>
            <!-- <ref bean="concurrentSessionStrategy" /> -->
            <ref bean="sessionFixationAuthenticationStrategy" />
            <ref bean="registerSessionStrategy" />
        </list>
    </constructor-arg>
</bean>


<!-- Remember-Me Service -->
<bean id="rememberMeServices" class="org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices">
    <constructor-arg name="key" value="${encoder.password}" />
    <constructor-arg name="userDetailsService" ref="userDetailsService" />
    <property name="parameter" value="j_remember_me" />
    <property name="cookieName" value="TMS_REME" />
</bean>

<!-- Filters -->
<bean id="usernamePasswordAuthenticationFilter" class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
    <property name="authenticationManager" ref="authenticationManager" />
    <property name="sessionAuthenticationStrategy" ref="sessionAuthenticationStrategy" />
    <property name="rememberMeServices" ref="rememberMeServices" />
    <property name="authenticationFailureHandler" ref="authenticationFailureHandler" />
</bean>

<bean id="sessionManagementFilter" class="org.springframework.security.web.session.SessionManagementFilter">
    <constructor-arg name="securityContextRepository" ref="securityContextRepository" />
    <constructor-arg name="sessionStrategy" ref="sessionAuthenticationStrategy" />
    <property name="invalidSessionStrategy" ref="facesRedirectStrategy" />
    <property name="authenticationFailureHandler" ref="authenticationFailureHandler" />
</bean>
 <bean id="concurrentSessionFilter" class="org.springframework.security.web.session.ConcurrentSessionFilter">
    <constructor-arg name="sessionRegistry" ref="sessionRegistry" />
    <constructor-arg name="expiredUrl" value="/auth/login/expiredDueToConcurrentAccess" />
    <property name="redirectStrategy" ref="facesRedirectStrategy" />
</bean>

<bean id="authEntryPoint" class="com.fg.ts.web.util.security.FacesLoginUrlAuthenticationEntryPoint">
    <property name="loginFormUrl" value="/auth/login"/>
</bean>
<!-- End Customization for Faces -->
<!-- ***************************** -->

<sec:http use-expressions="true" disable-url-rewriting="true" authentication-manager-ref="authenticationManager"
          entry-point-ref="authEntryPoint"> <!--security-context-repository-ref="securityContextRepository"-->
    <sec:http-basic />


    <sec:custom-filter ref="usernamePasswordAuthenticationFilter" before="FORM_LOGIN_FILTER" />
    <sec:custom-filter ref="sessionManagementFilter" position="SESSION_MANAGEMENT_FILTER" />
    <sec:anonymous />
    <sec:remember-me key="${encoder.password}" services-ref="rememberMeServices" />
    <sec:form-login login-page="/auth/login" authentication-failure-handler-ref="authenticationFailureHandler" />
    <sec:logout logout-url="/logout" invalidate-session="true" delete-cookies="TMS_SES,TMS_REME"
                logout-success-url="/auth/login" />


    <sec:session-management session-fixation-protection="none" />

    <!-- General Access Control -->
    <sec:intercept-url pattern="/projects/**" access="hasAnyRole('ROLE_ADMIN','ROLE_PM')" />
    <sec:intercept-url pattern="/assets/**" access="hasAnyRole('ROLE_ADMIN','ROLE_PM')" />
    <sec:intercept-url pattern="/setup/**" access="hasAnyRole('ROLE_ADMIN','ROLE_PM')" />
    <sec:intercept-url pattern="/tm/**" access="hasAnyRole('ROLE_ADMIN','ROLE_PM')" />
    <sec:intercept-url pattern="/termBase/**" access="hasAnyRole('ROLE_ADMIN','ROLE_PM')" />
    <sec:intercept-url pattern="/quality/**" access="hasAnyRole('ROLE_ADMIN','ROLE_PM')" />
    <sec:intercept-url pattern="/editor/**" access="isAuthenticated()" />
    <sec:intercept-url pattern="/api/**" access="isAuthenticated()" />
    <sec:intercept-url pattern="/**" access="isAuthenticated()" />
</sec:http>

How can I make spring security use the session created by spring-session ?

Upvotes: 3

Views: 4002

Answers (1)

engma
engma

Reputation: 1959

I found the issue, the reason was that in the FilterChain, springSecurityFilterChain Filter always loaded first before the springSessionRepositoryFilter, and so the Spring Session was not yet created, so the security filter created it's own.

this was happening because my springSessionRepositoryFilter was configured specifically for each servlet like this:

<filter>
    <filter-name>springSessionRepositoryFilter</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    <async-supported>true</async-supported>
</filter>
<filter-mapping>
    <filter-name>springSessionRepositoryFilter</filter-name>
    <servlet-name>mvc-dispatcher</servlet-name>
</filter-mapping>

but when I removed it and made it a global filter it then worked, much like this:

 <filter>
    <filter-name>springSessionRepositoryFilter</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    <async-supported>true</async-supported>
</filter>
<filter-mapping>
    <filter-name>springSessionRepositoryFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

Upvotes: 4

Related Questions