Search code examples
javaspringspring-mvcspring-securityspring-session

Setting up Spring Session and Redis via XML with existing Spring Security


I have inherited an existing web application that makes use of Spring components, including Spring Security.

I need to add the capability of having the application be session-less, for hosting in the cloud, and so I am attempting to enable Spring Session for this application. As a relative Spring newcomer, though, I am having some difficulty doing this.

There were no issues setting up Spring Data with Redis as precursor work to Spring Session, but following the Spring Session documentation at https://docs.spring.io/spring-session/docs/current/reference/html5/#httpsession-xml-spring-configuration has led me down a path of successive exceptions or internal server errors.

I'm posting all/relevant portions of the pom.xml, applicationContext.xml, servlet-context.xml, and web.xml files in hopes someone can tell me what I'm doing wrong, need to configure differently, or what I'm missing and still need to add.

The error I'm seeing is:

SEVERE [http-nio-8093-exec-42] org.apache.catalina.core.ApplicationContext.log StandardWrapper.Throwable
 java.lang.NoClassDefFoundError: com/lambdaworks/redis/RedisException
    at java.lang.Class.getDeclaredMethods0(Native Method)
    at java.lang.Class.privateGetDeclaredMethods(Class.java:2701)
    at java.lang.Class.getDeclaredMethods(Class.java:1975)
    at org.springframework.util.ReflectionUtils.getDeclaredMethods(ReflectionUtils.java:609)
    at org.springframework.util.ReflectionUtils.doWithMethods(ReflectionUtils.java:521)
    at org.springframework.util.ReflectionUtils.doWithMethods(ReflectionUtils.java:507)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.determineCandidateConstructors(AutowiredAnnotationBeanPostProcessor.java:241)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.determineConstructorsFromBeanPostProcessors(AbstractAutowireCapableBeanFactory.java:1069)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1042)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:510)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:772)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:839)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:538)
    at org.springframework.web.servlet.FrameworkServlet.configureAndRefreshWebApplicationContext(FrameworkServlet.java:667)
    at org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:633)
    at org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:681)
    at org.springframework.web.servlet.FrameworkServlet.initWebApplicationContext(FrameworkServlet.java:552)
    at org.springframework.web.servlet.FrameworkServlet.initServletBean(FrameworkServlet.java:493)
    at org.springframework.web.servlet.HttpServletBean.init(HttpServletBean.java:136)
    at javax.servlet.GenericServlet.init(GenericServlet.java:158)
    at org.apache.catalina.core.StandardWrapper.initServlet(StandardWrapper.java:1183)
    at org.apache.catalina.core.StandardWrapper.loadServlet(StandardWrapper.java:1099)
    at org.apache.catalina.core.StandardWrapper.allocate(StandardWrapper.java:779)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:133)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:478)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:80)
    at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:624)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342)
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:799)
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1455)
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.ClassNotFoundException: com.lambdaworks.redis.RedisException
    at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1285)
    at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1119)
    ... 45 more

I have found a suggestion online that I should include a dependency for lettuce in my pom.xml to resolve this:

<dependency>
  <groupId>biz.paluch.redis</groupId>
  <artifactId>lettuce</artifactId>
  <version>4.4.0.Final</version>
</dependency>

but including that dependency generates a different exception, which says that I now have multiple instances of RedisConnectionFactory:

org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [org.springframework.data.redis.connection.RedisConnectionFactory] is defined: expected single matching bean but found 2: org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory#0,redisConnectionFactory)

I would be very grateful if someone could assist me in figuring out what I'm doing wrong!

pom.xml (relevant sections)

<project xmlns="http://maven.apache.org/POM/4.0.0" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/xsd/maven-4.0.0.xsd">

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <httpcomponents-client.version>4.3.3</httpcomponents-client.version>
    <httpcomponents-core.version>4.3.3</httpcomponents-core.version>
    <spring.framework.version>4.2.5.RELEASE</spring.framework.version>
    <spring.security.version>4.1.0.RELEASE</spring.security.version>
</properties>

<repositories>
    <repository>
        <id>spring-milestones</id>
        <name>Spring Milestones</name>
        <url>https://repo.spring.io/libs-milestone</url>
        <snapshots>
            <enabled>false</enabled>
        </snapshots>
    </repository>
</repositories>

<dependencies>
    <!-- SPRING SECURITY -->
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-core</artifactId>
        <version>${spring.security.version}</version>
    </dependency> 
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-web</artifactId>
        <version>${spring.security.version}</version>
    </dependency> 
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-config</artifactId>
        <version>${spring.security.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-taglibs</artifactId>
        <version>${spring.security.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-ldap</artifactId>
        <version>${spring.security.version}</version>
    </dependency>

    <!-- SPRING -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>${spring.framework.version}</version>
        <exclusions>
            <exclusion>
                <groupId>commons-logging</groupId>
                <artifactId>commons-logging</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>${spring.framework.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context-support</artifactId>
        <version>${spring.framework.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>${spring.framework.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-beans</artifactId>
        <version>${spring.framework.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aop</artifactId>
        <version>${spring.framework.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-tx</artifactId>
        <version>${spring.framework.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-expression</artifactId>
        <version>${spring.framework.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-web</artifactId>
        <version>${spring.framework.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>${spring.framework.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
        <version>${spring.framework.version}</version>
    </dependency>

    <!-- SPRING DATA -->
    <dependency>
        <groupId>org.springframework.data</groupId>
        <artifactId>spring-data-jpa</artifactId>
        <version>1.10.1.RELEASE</version>
        <exclusions>
            <exclusion>
                <groupId>org.springframework</groupId>
                <artifactId>spring-core</artifactId>
            </exclusion>
            <exclusion>
                <artifactId>spring-context</artifactId>
                <groupId>org.springframework</groupId>
            </exclusion>
            <exclusion>
                <groupId>org.springframework</groupId>
                <artifactId>spring-aop</artifactId>
            </exclusion>
            <exclusion>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-api</artifactId>
            </exclusion>
            <exclusion>
                <groupId>org.slf4j</groupId>
                <artifactId>jcl-over-slf4j</artifactId>
            </exclusion>
        </exclusions>
    </dependency>

    <!-- SPRING DATA WITH REDIS -->
    <dependency>
        <groupId>org.springframework.data</groupId>
        <artifactId>spring-data-redis</artifactId>
        <version>1.8.6.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>redis.clients</groupId>
        <artifactId>jedis</artifactId>
        <version>2.9.0</version>
    </dependency>

    <!--  SPRING SESSION  -->
    <dependency>
        <groupId>org.springframework.session</groupId>
        <artifactId>spring-session</artifactId>
        <version>1.3.1.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.session</groupId>
        <artifactId>spring-session-data-redis</artifactId>
        <version>1.3.1.RELEASE</version>
        <type>pom</type>
    </dependency>
  </dependencies>
</project>

applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:security="http://www.springframework.org/schema/security"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd
                http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">

    <context:property-placeholder location="classpath:app.properties" />

    <bean id="usingAuthentication" class="java.lang.Boolean">
        <description>Flag indicating if authentication is being used.</description>
        <constructor-arg>
            <value>${usingAuthentication}</value>
        </constructor-arg>
    </bean>

    <bean id="jaasmineAppName" class="java.lang.String">
        <description>The name of the JAAS configuration to use when sending requests to the web api.</description>
        <constructor-arg>
            <value>jaasmine.login</value>
        </constructor-arg>
    </bean>

    <bean id="authenticationService" class="com.logiclander.jaasmine.authentication.KeyTabAuthenticationService">
        <description>Obtains kerberos tickets when the kerberosClientPrincipal sends requests to the web api.</description>
        <constructor-arg ref="jaasmineAppName"/>
    </bean>

    <!-- HTTP CLIENT -->
    <bean id="poolingConnectionManager"
        class="jaxrs.ext.http4client.CustomClientConnectionManager">
        <property name="maxTotalConnection" value="200" />
        <property name="maxTotalPerRoute" value="20" />
    </bean>

    <bean id="restHttpClient"
        class="jaxrs.ext.http4client.CustomHttpClient"
        scope="prototype">
        <constructor-arg name="connManager" ref="poolingConnectionManager" />
        <property name="sslv3" value="false"/>
    </bean>

    <bean id="clientExecutor" class="org.jboss.resteasy.client.core.executors.ApacheHttpClient4Executor">
        <constructor-arg name="httpClient" ref="restHttpClient"/>
    </bean>

    <!-- HTTP CLIENT FOR NVPAPI -->
    <bean id="nvpUserPwdCredential"
        class="org.apache.http.auth.UsernamePasswordCredentials">
        <constructor-arg name="userName"> <value>xyzf</value> </constructor-arg>
        <constructor-arg name="password"> <value>xxxx</value> </constructor-arg>
    </bean>

    <bean id="nvpPoolingConnectionManager"
           class="jaxrs.ext.http4client.CustomClientConnectionManager">
        <property name="maxTotalConnection" value="200" />
        <property name="maxTotalPerRoute" value="20" />
    </bean>

    <bean id="nvpRestHttpClient"
        class="jaxrs.ext.http4client.CustomHttpClient"
        scope="prototype">
        <constructor-arg name="connManager" ref="nvpPoolingConnectionManager" />
        <property name="credentials" ref="nvpUserPwdCredential"/>
        <property name="sslv3" value="false"/>
    </bean>

    <bean id="nvpClientExecutor"
        class="org.jboss.resteasy.client.core.executors.ApacheHttpClient4Executor">
        <constructor-arg name="httpClient" ref="nvpRestHttpClient"/>
    </bean>

    <!--
    |
    |  REDIS
    |
    -->
    <bean id="stringSerializer" class="org.springframework.data.redis.serializer.StringRedisSerializer"/>

    <bean id="redisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" p:host-name="localhost" p:port="6379" />

    <!-- redis template definition -->
    <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate" p:connection-factory-ref="redisConnectionFactory">
        <property name="keySerializer" ref="stringSerializer"/>
        <property name="valueSerializer" ref="stringSerializer"/>
    </bean>

    <!--
    |
    |  SPRING SECURITY
    |
    -->

    <bean id="http403EntryPoint" class="org.springframework.security.web.authentication.Http403ForbiddenEntryPoint" />

    <security:http auto-config="false" entry-point-ref="http403EntryPoint" use-expressions="true" disable-url-rewriting="true">
        <security:intercept-url pattern="/logout/**" access="permitAll" requires-channel="${securityChannel}" />
        <security:intercept-url pattern="/admin/**" access="hasRole('ROLE_ADMIN')" requires-channel="${securityChannel}"/>
        <security:intercept-url pattern="/user/**" access="hasAnyRole('ROLE_STUDENT','ROLE_STAFF')" requires-channel="${securityChannel}"/>
        <security:intercept-url pattern="/**" access="permitAll" requires-channel="${securityChannel}"/>
        <security:custom-filter position="PRE_AUTH_FILTER" ref="${cPreAuthFilter}" />
        <security:custom-filter position="LOGOUT_FILTER" ref="logoutFilter" /> 
        <security:csrf disabled="true" />
    </security:http>

    <bean id="workstationFilter"
          class="webmvc.security.WorkstationPreAuthFilter">
        <property name="authenticationManager" ref="authenticationManager"/>
    </bean>

    <security:authentication-manager alias="authenticationManager">
        <security:authentication-provider ref="preauthAuthProvider"/>
    </security:authentication-manager>

    <bean id="preauthAuthProvider"
          class="org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider">
        <property name="preAuthenticatedUserDetailsService">
            <bean id="userDetailsServiceWrapper"
                  class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper">
                <property name="userDetailsService" ref="${appUserDetailsService}"/>
            </bean>
        </property>
    </bean>

    <bean id="mockUserDetailsService" 
        class="webmvc.security.MockAdUserDetailsService">
    </bean>

    <bean id="adUserDetailsService"
        class="org.springframework.security.ldap.userdetails.LdapUserDetailsService">
        <constructor-arg ref="ldapSearch"/>
        <constructor-arg ref="ldapAuthoritiesPopulator"/>
        <property name="userDetailsMapper" ref="activeDirectoryUserDetailsContextMapper"/>
    </bean>

    <bean id="ldapSearch"
                class="org.springframework.security.ldap.search.FilterBasedLdapUserSearch">
        <constructor-arg value="OU=People,dc=ad,dc=uil,dc=edu"/> <!-- use-search-base -->
        <constructor-arg value="(sAMAccountName={0})"/> <!-- user-search-filter -->
        <constructor-arg ref="ldapServer"/>
    </bean>

    <bean id="ldapAuthoritiesPopulator"
                class="org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator">
        <constructor-arg ref="ldapServer"/>
        <constructor-arg value="ou=ci,ou=georgia,ou=Services,ou=SERVICES,ou=City,dc=ad,dc=uil,dc=edu"/> <!-- group search base -->
        <property name="groupRoleAttribute" value="cn"/> <!-- cn is default -->
        <property name="rolePrefix" value="" /> <!-- default is ROLE_ -->
        <property name="convertToUpperCase" value="false"/>
        <property name="searchSubtree" value="true"/> <!-- deep search -->
        <property name="groupSearchFilter" value="(member:1.2.840.113556.1.4.1941:={0})"/> <!-- MAGIC -->
    </bean>

    <bean id="ldapServer"
                class="org.springframework.security.ldap.DefaultSpringSecurityContextSource">
        <constructor-arg value="ldaps://ad.business.com:636"/>
        <property name="userDn" value="cn=c-webmvc,ou=service-accounts,ou=georgia,ou=CI-Services,ou=CI,ou=City,dc=ad,dc=uil,dc=edu"/>
        <property name="password" value="xxxx"/>
    </bean>

    <bean id="activeDirectoryUserDetailsContextMapper"
            class="webmvc.security.ActiveDirectoryUserDetailsContextMapper">
    </bean>

    <!--
    |
    |    LOGOUT CONFIGS
    |
    -->
    <bean id="logoutFilter" class="org.springframework.security.web.authentication.logout.LogoutFilter">
        <constructor-arg index="0" value="${cLogoutUri}" />
        <constructor-arg index="1">
            <list>
                <ref bean="securityContextLogoutHandler" />
                <ref bean="cookieClearingLogoutHandler" />
            </list>
        </constructor-arg>
        <property name="filterProcessesUrl" value="/logout" />
    </bean>

    <bean id="securityContextLogoutHandler" 
                class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler">
        <property name="invalidateHttpSession" value="true"/>
    </bean>

    <bean id="cookieClearingLogoutHandler" class="org.springframework.security.web.authentication.logout.CookieClearingLogoutHandler">
        <constructor-arg>
            <!-- Names of the cookies you want to remove when user logs out -->
            <list>
                <value>JSESSIONID</value>
            </list>
        </constructor-arg>
    </bean>

</beans>

servlet-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:context="http://www.springframework.org/schema/context"
   xmlns:mvc="http://www.springframework.org/schema/mvc"
   xmlns:util="http://www.springframework.org/schema/util"
   xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
    http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">

<context:annotation-config />

<context:component-scan base-package="edu.illinois.cis.webmvc" />

<!-- expose property file to controllers -->
<util:properties id="defaultProps" location="classpath:default.properties"/>
<util:properties id="appProps" location="classpath:app.properties"/>        

<mvc:annotation-driven />
<mvc:resources mapping="/static/**" location="/static/" cache-period="3600"/>

<bean id="viewProps" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
    <property name="locations">
        <list>
            <value>classpath:default.properties</value>
            <value>classpath:gened.properties</value>
            <value>classpath:app.properties</value>
        </list>
    </property>
</bean>

<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/views/"/>
    <property name="suffix" value=".jsp"/>
    <property name="exposedContextBeanNames">
        <list>
            <value>viewProps</value>
        </list>
    </property>
</bean>

<bean id="messageSource"
                  class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
    <property name="basename" value="classpath:messages" />
    <property name="defaultEncoding" value="UTF-8" />
    <property name="cacheSeconds" value="5" />
</bean>

<!-- Use the combination of <context:annotation-config/> (above) 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.  -->
  <bean class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration"/>

  <!-- Create a RedisConnectionFactory that connects Spring Session to the Redis Server. 
     Configure the connection to connect to localhost on the default port (6379) -->
  <bean class="org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory"/>
</beans>

web.xml

<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">

<servlet>
  <servlet-name>courses</servlet-name>
  <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  <init-param>
    <param-name>contextConfigLocation</param-name>
      <param-value>/WEB-INF/spring/servlet-context.xml</param-value>
  </init-param>
</servlet>

<servlet-mapping>
    <servlet-name>courses</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

<!-- load applicationContext.xml -->
<listener>
    <listener-class>
        org.springframework.web.context.ContextLoaderListener
    </listener-class>
</listener>

<!-- ensure that the Servlet Container (i.e. Tomcat) uses the springSessionRepositoryFilter for every request -->
<!-- DelegatingFilterProxy will look up a Bean by the name of springSessionRepositoryFilter and cast it to a Filter -->
<!-- For every request that DelegatingFilterProxy is invoked, the springSessionRepositoryFilter will be invoked -->
<filter>
  <filter-name>springSessionRepositoryFilter</filter-name>
  <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>

<filter-mapping>
  <filter-name>springSessionRepositoryFilter</filter-name>
  <url-pattern>/*</url-pattern>
  <dispatcher>REQUEST</dispatcher>
  <dispatcher>ERROR</dispatcher>
</filter-mapping>

<!-- Spring Security -->
<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>

<error-page>
  <error-code>404</error-code>
  <location>/error/404</location>
</error-page>
<error-page>
  <error-code>403</error-code>
  <location>/error/403</location>
</error-page>


Solution

  • org.springframework.beans.factory.NoUniqueBeanDefinitionException: 
    No qualifying bean of type 
    [org.springframework.data.redis.connection.RedisConnectionFactory] is 
    defined: expected single matching bean but found 2: 
    org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory#0
    ,redisConnectionFactory)
    

    The exception means that spring is waiting for a bean of type RedisConnectionFactory but there is no way to define which one is the bean to use, then I think that it is happening because at applicationContext.xml there is a bean cleared as

    <bean id="redisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" p:host-name="localhost" p:port="6379" />
    

    but also in servlet-context.xml there is a bean declared as:

    <bean class="org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory"/>
    

    And according to the javadoc of JedisConnectionFactory and LettuceConnectionFactory both of them implements the RedisConnectionFactory interface.

    My conclusion is that RedisHttpSessionConfiguration is expecting a bean of type RedisConnectionFactory but it couldn't be injected since there are two RedisConnectionFactory beans. Based on that you can try just removing the LettuceConnectionFactory bean from servlet-context.xml since redisConnectionFactory bean is of type RedisConnectionFactory and is already created from applicationContext.xml.

    I hope this info could help you to solve this issue.