Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use Spring Session + Spring security xml configuration and multiply security filter

Background

Hey all, We have Spring project which uses Spring security. We have defined the security filters by defining

 <b:bean id="springSecurityFilterChain" class="org.springframework.security.web.FilterChainProxy">

whith filter-chain-map

and in the web.xml we do

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

 <filter-mapping>
    <filter-name>springSecurityFilterChain</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

and it all works well :). Now when hooking up Spring session with redis according to the doc the next following lines

<context:annotation-config />
<bean class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration"/>

create a filter named springSessionRepositoryFilter. So basically what we did is in every custom filter-chain we add that filter to be the very first filter . i.e:

<b:bean id="springSecurityFilterChain"   class="org.springframework.security.web.FilterChainProxy">
     <filter-chain-map request-matcher="ant">

           <filter-chain pattern="/api/someapieformobilelogin" filters="none" />  <!-- no filter on login -->
        <filter-chain pattern="/api/**"
            filters="springSessionRepositoryFilter, securityContextFilter,and some other spring security filter />

        <filter-chain pattern="/**"
            filters="springSessionRepositoryFilter, securityContextFilter,and some other spring security filter />

The results: the app seems to work good and also monitoring via redis-cli shows the spring is communicating with redis.

The question

Does the use of springSessionRepositoryFilter inside the filter-chain is ok? or we abused the filtering system?

Thanks,

Oak

Edit

It seems that above will not work for the case one wants to Authenticate the user from code i.e

Authentication authentication = authenticationManager
                .authenticate(authenticationToken);
SecurityContext securityContext = SecurityContextHolder
                .getContext();
securityContext.setAuthentication(authentication);

will failed. Maybe because its not enough to run it via filter-chain of org.springframework.security.web.FilterChainProxy.

What do you think on run it as filter in web.xml?

<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>

<filter-mapping>
    <filter-name>springSessionRepositoryFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

<filter-mapping>
    <filter-name>springSecurityFilterChain</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

The above will force running springSessionRepositoryFilter before springSecurityFilterChain but in this example org.springframework.web.filter.DelegatingFilterProxy is being called twice. any other ways to make springSessionRepositoryFilter run as a filter before out springSecurityFilterChain filter?

like image 513
oak Avatar asked Sep 08 '15 13:09

oak


2 Answers

According to my testing, springSessionRepositoryFilter must run first. This is due the fact that springSessionRepositoryFilter replaces the HttpSession implementation. Here is my solution using xml files.

redis-cache.xml

<context:annotation-config />
<bean
    class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration" />

<bean
    class="org.springframework.security.web.session.HttpSessionEventPublisher" />

<!-- end of seesion managment configuration -->


<bean id="redisConnectionFactory"
    class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
    <property name="port" value="${app.redis.port}" />
    <property name="hostName" value="${app.redis.hostname}" />
    <property name="password" value="${app.redis.password}" />
    <property name="usePool" value="true" />
</bean>

We use the combination of and RedisHttpSessionConfiguration because Spring Session does not yet provide XML Namespace support (see gh-104). This creates a Spring Bean with the name of springSessionRepositoryFilter that implements Filter. The filter is what is in charge of replacing the HttpSession implementation to be backed by Spring Session. In this instance Spring Session is backed by Redis. source

Now, as we have the session filter named springSessionRepositoryFilter it must run as the first filter because it replaces the HttpSession implementation.

In order to do so, we declare it as the first filter in web.xml. For more info about filters and filter's order checkout the docs

web.xml

<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>

<filter-mapping>
    <filter-name>springSessionRepositoryFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

<filter-mapping>
    <filter-name>CharacterEncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

<filter-mapping>
    <filter-name>springSecurityFilterChain</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>
    /WEB-INF/redis-cache.xml
    </param-value>
</context-param>

 <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

Note that the first to run is springSessionRepositoryFilter. But actully org.springframework.web.filter.DelegatingFilterProxy class is running and it looks for the filter by the bean's name. So it search for the bean created by our early configurations. reference

The extra line about the redis-cache.xml are important as well. Otherwise our spring application context cannot know about our redis configuration

reference

like image 125
oak Avatar answered Dec 28 '22 08:12

oak


It does not matter. From the Javadoc:

The SessionRepositoryFilter must be placed before any Filter that access the HttpSession or that might commit the response to ensure the session is overridden and persisted properly.

So long as you add springSessionRepositoryFilter before anything that can commit the response or access the HttpSession, you are fine. In the case of Spring Security the main thing you will want to ensure is that springSessionRepositoryFilter is before the SecurityContextPersistenceFilter. This can be done by including springSessionRepositoryFilter within the container or within Spring Security's FilterChainProxy (i.e. <filter-chain>).

like image 20
Rob Winch Avatar answered Dec 28 '22 06:12

Rob Winch