如何使用 Spring Security 和 Spring Session 从多个服务器获得相同的会话

How to get same session with Spring Security and Spring Session From multiple server

对不起,我的英语还是不太好。 请耐心等待,我希望你能理解我的问题..


我有两个网络服务器。 (每个网络应用程序都相同)

Web 服务器共享一台 Redis 服务器。 我使用 Spring 安全和 Spring 会话。 当我登录第一台服务器并访问第二台服务器时, 我想自动登录第二个服务器,但是没有。

我猜的,因为session id不同服务器ip不一样

WEB.XML

<!-- The definition of the Root Spring Container shared by all Servlets 
    and Filters -->
<!-- Loads Spring Security config file -->
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>
        /WEB-INF/spring/root-context.xml,
        /WEB-INF/spring/spring-security.xml,
        /WEB-INF/spring/jedis.xml
            </param-value>
</context-param>

<!-- Creates the Spring Container shared by all Servlets and Filters -->
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<!-- Processes application requests -->
<servlet>
    <servlet-name>appServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

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

<!-- Encoding -->
<filter>
    <filter-name>CharacterEncodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
        <param-name>encoding</param-name>
        <param-value>utf-8</param-value>
    </init-param>
    <init-param>
        <param-name>forceEncoding</param-name>
        <param-value>true</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>CharacterEncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

<!-- Session Filter -->
<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>
</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>

jedis.xml

<bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
    <property name="hostName" value=""<!-- My Server IP --> />
    <property name="port" value="6379" />
    <property name="poolConfig" ref="redisPoolConfig" />
</bean>


<bean id="redisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
    <property name="testOnBorrow" value="true" />
    <property name="minEvictableIdleTimeMillis" value="60000" />
    <property name="softMinEvictableIdleTimeMillis" value="1800000" />
    <property name="numTestsPerEvictionRun" value="-1" />
    <property name="testOnReturn" value="false" />
    <property name="testWhileIdle" value="true" />
    <property name="timeBetweenEvictionRunsMillis" value="30000" />
</bean>

<!-- string serializer to make redis key more readible  -->
<bean id="stringRedisSerializer"
    class="org.springframework.data.redis.serializer.StringRedisSerializer" />

<!-- redis template definition  -->
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate" >
    <property name="connectionFactory" ref="jedisConnectionFactory" />
    <property name="keySerializer" ref="stringRedisSerializer" />
    <property name="hashKeySerializer" ref="stringRedisSerializer" />
</bean>

spring-security.xml

<http pattern="/resources/**" security="none" />

<http auto-config="true" >
    <session-management session-fixation-protection="changeSessionId">
        <concurrency-control max-sessions="1" error-if-maximum-exceeded="true"/> <!-- I couldn't clear understand of this element-->
    </session-management>
    <intercept-url pattern="/" access="ROLE_ANONYMOUS, ROLE_USER" />
    <intercept-url pattern="/perBoard" access="ROLE_ANONYMOUS, ROLE_USER" />
    <intercept-url pattern="/per" access="ROLE_ANONYMOUS, ROLE_USER" />
    <intercept-url pattern="/perSearchTag" access="ROLE_ANONYMOUS, ROLE_USER" />
    <intercept-url pattern="/**" access="ROLE_USER" />
    <form-login login-page="/" 
                authentication-success-handler-ref="loginSuccessHandler"
                authentication-failure-handler-ref="loginFailureHandler"
                always-use-default-target="true" 
                username-parameter="j_username" 
                password-parameter="j_password"/>
                <!-- default-target-url="/board"  -->
    <logout logout-success-url="/" invalidate-session="true" delete-cookies="true" />
</http>

<authentication-manager>
    <authentication-provider user-service-ref="userDetailsService" />
</authentication-manager>

<beans:bean id="loginSuccessHandler" class=".......LoginSuccessHandler">
    <beans:property name="sqlSession" ref="sqlSession" />
</beans:bean>
<beans:bean id="loginFailureHandler" class=".......LoginFailureHandler" />
<beans:bean id="userDetailsService" class="......UserDetailsServiceImpl">
    <beans:property name="sqlSession" ref="sqlSession" />
</beans:bean>

看来是个老问题了。但是,看起来有可能实现想要的行为。查看 http://docs.spring.io/spring-session/docs/current/reference/html5/guides/security.html 了解更多详情

创建redis beans

<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}" />
</bean>

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

以上将创建与 Redis 服务器的连接,并将创建一个名为 springSessionRepositoryFilter 的 bean,它将替换常规的 HttpSession 实现。

设置spring安全

可以通过使用 org.springframework.security.web.FilterChainProxy 创建 spring filter 即:

<b:bean id="springSecurityFilterChain" class="org.springframework.security.web.FilterChainProxy">
    <filter-chain-map request-matcher="ant">
         <filter-chain pattern="/somelocation/" filters="none" />
         <filter-chain pattern="/someotherlocation"
            filters="springSessionRepositoryFilter, somemorespring filters"/>
    </filter-chain-map>
</b:bean>

注意:spring security 的过滤器顺序很重要,未包含在本答案中。但是为了能够与 spring Session 一起工作 和 Redis,第一个过滤器必须是 springSessionRepositoryFilter。有关更多信息,请访问 http://docs.spring.io/spring-security/site/docs/3.0.x/reference/security-filter-chain.html

正在设置 Http 会话

编辑web.xml

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

这将允许 tomcat 在任何过滤器之前使用 springSecurityFilterChain,因此它将允许 springSessionRepositoryFilter 成为第一个过滤器。这将导致 Spring session 魔法从 redis db

获得 session

Using Spring session + spring security with out custom spring filters 可以在 http://www.jayway.com/2015/05/31/scaling-out-with-spring-session/

找到