Spring security 中包含对Session的处理,在引入Spring session后,
两者是如何共存的。
有几个问题需要考虑:
1、两者Session存放的位置
2、Session的生命周期管理
第一个问题还没了解清楚,我暂时将问题记录下了,如果有高手知道可以私信我。
疑问如下:
在xml中security配置(session部分)如下:
<security:http>
<security:session-management invalid-session-url="/login.html?error=1" session-authentication-strategy-ref="sas" />
</security:http>
<bean id="sas" class="org.springframework.security.web.authentication.session.CompositeSessionAuthenticationStrategy">
<constructor-arg>
<list>
<bean class="org.springframework.security.web.authentication.session.ConcurrentSessionControlAuthenticationStrategy">
<constructor-arg ref="sessionRegistry"/>
</bean>
<bean class="org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy"/>
<bean class="org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy">
<constructor-arg ref="sessionRegistry"/>
</bean>
</list>
</constructor-arg>
</bean>
<bean id="sessionRegistry" class="org.springframework.security.core.session.SessionRegistryImpl" />
其中SessionRegistryImpl的部分代码:
/** <principal:Object,SessionIdSet> */
private final ConcurrentMap<Object, Set<String>> principals = new ConcurrentHashMap<Object, Set<String>>();
/** <sessionId:Object,SessionInformation> */
private final Map<String, SessionInformation> sessionIds = new ConcurrentHashMap<String, SessionInformation>();
明显是将一些user的信息存放在内存中的。
那么SpringSession是将Session信息放到Redis中的,两者矛盾,怎样做才能将SessionRegistryImpl中的信息存放到Redis中?
我能想到的是自己重写这部分,将principals、sessionIds信息写到Redis中。
第二个问题:Session生命周期
在Spring security中通过ApplicationEvent管理生命周期的,而SpringSession在session超时或者被删除时会发送ApplicationEvent。通过代码来看看:
首先在xml中会有如下配置:
<context:annotation-config/>
<bean class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration">
<property name="maxInactiveIntervalInSeconds" value="300"/>
</bean>
在RedisHttpSessionConfiguration的构造函数中:
@Bean
public RedisMessageListenerContainer redisMessageListenerContainer(
RedisConnectionFactory connectionFactory) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
container.addMessageListener(redisSessionMessageListener(),
Arrays.asList(new PatternTopic("__keyevent@*:del"),new PatternTopic("__keyevent@*:expired")));
return container;
}
其中redisSessionMessageListener的类集成了MessageListener接口,熟悉redis的知道,这个接口时redis中的mq的监听接口,也就是说spring session在察觉Session过期或者被删除了的话,会广播一个消息,redisSessionMessageListener就会接受到这个消息,处理这个消息的函数如下:
public void onMessage(Message message, byte[] pattern) {
byte[] messageChannel = message.getChannel();
byte[] messageBody = message.getBody();
if(messageChannel == null || messageBody == null) {
return;
}
String channel = new String(messageChannel);
if(!(channel.endsWith(":del") || channel.endsWith(":expired"))) {
return;
}
String body = new String(messageBody);
if(!body.startsWith("spring:session:sessions:")) {
return;
}
int beginIndex = body.lastIndexOf(":") + 1;
int endIndex = body.length();
String sessionId = body.substring(beginIndex, endIndex);
if(logger.isDebugEnabled()) {
logger.debug("Publishing SessionDestroyedEvent for session " + sessionId);
}
publishEvent(new SessionDestroyedEvent(this, sessionId));
}
上面最后一句就是发送一个SessionDestroyedEvent,这个Event就是ApplicationEvent。再通过如下配置Spring Security就能接收到这个消息了。
1、Spring security可以在通过配置设定超时返回的页面。
<security:session-management invalid-session-url="/login.html?error=1" session-authentication-strategy-ref="sas" />
2、在web.xml中配置
<listener> <listener-class> org.springframework.security.web.session.HttpSessionEventPublisher </listener-class> </listener>
这样spring security会监听session的listener。
到这里就知道Spring Security 和Spring Session是如何协作的了。
以上。