通过 XML 使用现有 Spring 安全性设置 Spring 会话和 Redis
Setting up Spring Session and Redis via XML with existing Spring Security
我继承了一个现有的 Web 应用程序,该应用程序使用了 Spring 个组件,包括 Spring 安全性。
我需要添加使应用程序无会话的功能,以便在云中托管,因此我尝试为此应用程序启用 Spring 会话。不过,作为一个亲戚 Spring 新人,我在做这件事时遇到了一些困难。
使用 Redis 设置 Spring 数据作为 Spring 会话的前身工作没有问题,但是遵循 https://docs.spring.io/spring-session/docs/current/reference/html5/#httpsession-xml-spring-configuration 的 Spring 会话文档让我失望了连续异常或内部服务器错误的路径。
我正在发布 pom.xml、applicationContext.xml、servlet-context.xml 和 web.xml 文件的 all/relevant 部分,希望有人能告诉我我做错了什么,需要不同的配置,或者我遗漏了什么但仍然需要添加。
我看到的错误是:
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.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
我在网上找到了一个建议,我应该在我的 pom.xml 中加入对生菜的依赖来解决这个问题:
<dependency>
<groupId>biz.paluch.redis</groupId>
<artifactId>lettuce</artifactId>
<version>4.4.0.Final</version>
</dependency>
但包括该依赖项会生成一个不同的异常,它表示我现在有多个 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)
如果有人能帮助我找出我做错了什么,我将不胜感激!
pom.xml(相关部分)
<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>
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)
异常意味着 spring 正在等待类型为 RedisConnectionFactory
的 bean,但是没有办法定义要使用的 bean,那么我认为它正在发生,因为在applicationContext.xml
有一个 bean 被清除为
<bean id="redisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" p:host-name="localhost" p:port="6379" />
而且在 servlet-context.xml
中还有一个 bean 声明为:
<bean class="org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory"/>
并且根据 JedisConnectionFactory
和 LettuceConnectionFactory
的 javadoc,它们都实现了 RedisConnectionFactory
接口。
我的结论是 RedisHttpSessionConfiguration
需要一个类型为 RedisConnectionFactory
的 bean,但它无法注入,因为有两个 RedisConnectionFactory
bean。基于此,您可以尝试从 servlet-context.xml
中删除 LettuceConnectionFactory
bean,因为 redisConnectionFactory
bean 的类型为 RedisConnectionFactory
并且已经从 applicationContext.xml
.[=27 创建了=]
我希望这些信息可以帮助您解决这个问题。
我继承了一个现有的 Web 应用程序,该应用程序使用了 Spring 个组件,包括 Spring 安全性。
我需要添加使应用程序无会话的功能,以便在云中托管,因此我尝试为此应用程序启用 Spring 会话。不过,作为一个亲戚 Spring 新人,我在做这件事时遇到了一些困难。
使用 Redis 设置 Spring 数据作为 Spring 会话的前身工作没有问题,但是遵循 https://docs.spring.io/spring-session/docs/current/reference/html5/#httpsession-xml-spring-configuration 的 Spring 会话文档让我失望了连续异常或内部服务器错误的路径。
我正在发布 pom.xml、applicationContext.xml、servlet-context.xml 和 web.xml 文件的 all/relevant 部分,希望有人能告诉我我做错了什么,需要不同的配置,或者我遗漏了什么但仍然需要添加。
我看到的错误是:
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.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
我在网上找到了一个建议,我应该在我的 pom.xml 中加入对生菜的依赖来解决这个问题:
<dependency>
<groupId>biz.paluch.redis</groupId>
<artifactId>lettuce</artifactId>
<version>4.4.0.Final</version>
</dependency>
但包括该依赖项会生成一个不同的异常,它表示我现在有多个 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)
如果有人能帮助我找出我做错了什么,我将不胜感激!
pom.xml(相关部分)
<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>
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)
异常意味着 spring 正在等待类型为 RedisConnectionFactory
的 bean,但是没有办法定义要使用的 bean,那么我认为它正在发生,因为在applicationContext.xml
有一个 bean 被清除为
<bean id="redisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" p:host-name="localhost" p:port="6379" />
而且在 servlet-context.xml
中还有一个 bean 声明为:
<bean class="org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory"/>
并且根据 JedisConnectionFactory
和 LettuceConnectionFactory
的 javadoc,它们都实现了 RedisConnectionFactory
接口。
我的结论是 RedisHttpSessionConfiguration
需要一个类型为 RedisConnectionFactory
的 bean,但它无法注入,因为有两个 RedisConnectionFactory
bean。基于此,您可以尝试从 servlet-context.xml
中删除 LettuceConnectionFactory
bean,因为 redisConnectionFactory
bean 的类型为 RedisConnectionFactory
并且已经从 applicationContext.xml
.[=27 创建了=]
我希望这些信息可以帮助您解决这个问题。