前言
上一节实现session并发的时候,注意到一个细节
//注册session
sessionRegistry.registerNewSession(request.getSession(true).getId(), principal);
为什么要手动注册一个session呢,web服务器不是默认给每个独立的请求创建一个session吗?不注册的话,会怎么样?
分析
(1)首先先聊下web服务器里的session创建时机,当一个新的请求到来时,HttpSessionListener接口(实现的监听器)的sessionCreated方法就会被调用,为之创建一个session。
/**
* Handles the HttpSessionEvent by publishing a {@link HttpSessionCreatedEvent} to the
* application appContext.
*
* @param event HttpSessionEvent passed in by the container
*/
public void sessionCreated(HttpSessionEvent event) {
HttpSessionCreatedEvent e = new HttpSessionCreatedEvent(event.getSession());
Log log = LogFactory.getLog(LOGGER_NAME);
if (log.isDebugEnabled()) {
log.debug("Publishing event: " + e);
}
getContext(event.getSession().getServletContext()).publishEvent(e);
}
(2)测试来验证下
@Configuration
public class HttpSessionConfig {
private static final Map<String, HttpSession> sessions = new HashMap<>();
public List<HttpSession> getActiveSessions() {
return new ArrayList<>(sessions.values());
}
@Bean
public HttpSessionListener httpSessionListener() {
return new HttpSessionListener() {
@Override
public void sessionCreated(HttpSessionEvent hse) {
sessions.put(hse.getSession().getId(), hse.getSession());
}
@Override
public void sessionDestroyed(HttpSessionEvent hse) {
sessions.remove(hse.getSession().getId());
}
};
}
}
每建立起一个会话就会创建一个session
(3)spring security session的创建时机有4种
@Override
protected void configure(HttpSecurity http) throws Exception {
http.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
}
always – 如果session不存在总是需要创建;
ifRequired – 仅当需要时,创建session(默认配置);
never – 框架从不创建session,但如果已经存在,会使用该session ;
stateless – Spring Security不会创建session,或使用session;
举例:如果设置成always,判断的地方是在SecurityContextPersistenceFilter#doFilter
if (forceEagerSessionCreation) {
HttpSession session = request.getSession();
if (debug && session.isNew()) {
logger.debug("Eagerly created session: " + session.getId());
}
}
参考:spring security控制session
(4)所以spring security默认是不会自动创建session,但是spring security里面提供了注册session的方法SessionRegistryImpl#registerNewSession,session是交由SessionRegistryImpl去管理。下面是注册session的方法
sessionIds.put(sessionId,
new SessionInformation(principal, sessionId, new Date()));
可以看到这里存储的session其实是SessionInformation,并不是web容器里的HttpSession,这是两个不同的session,要注意区分。
(5)到这谜团解开了,SessionRegistryImpl是security提供来管理SessionInformation的,所以如果我们不手动注册,则SessionRegistryImpl#getAllPrincipals永远为空即注册表里永远为空,这样也就无法实现session的并发处理。
总结
这是自己翻看源码的理解,若有错误请及时联系我更正!