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.
Does the use of springSessionRepositoryFilter inside the filter-chain is ok? or we abused the filtering system?
Thanks,
Oak
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?
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.
<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
<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
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>).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With