我想将Spring Webflux项目从Spring Boot 2.0.1升级到Spring Boot 2.0.3。在我的项目中,我的会话由Spring Session Data Redis支持。升级Spring Boot版本时,我注意到Spring Boot 2.0.2+中的websession无效问题。
在Spring Boot 2.0.1中,我通过以下方式使会话无效:
webSession.invalidate().subscribe();
这导致我当前的会话被破坏,并使用新的会话ID,创建时间等生成了一个新会话。
但是,从Spring Boot 2.0.2开始,相同的代码似乎并没有完全破坏会话。当前会话信息只是被“清除”,并且之后仍使用相同的会话ID。这是一个问题,因为它在 ReactiveRedisOperationsSessionRepository.class 中导致空指针异常:
private static final class SessionMapper implements Function<Map<String, Object>, MapSession> {
private final String id;
private SessionMapper(String id) {
this.id = id;
}
public MapSession apply(Map<String, Object> map) {
MapSession session = new MapSession(this.id);
session.setCreationTime(Instant.ofEpochMilli((Long)map.get("creationTime")));
session.setLastAccessedTime(Instant.ofEpochMilli((Long)map.get("lastAccessedTime")));
session.setMaxInactiveInterval(Duration.ofSeconds((long)(Integer)map.get("maxInactiveInterval")));
map.forEach((name, value) -> {
if (name.startsWith("sessionAttr:")) {
session.setAttribute(name.substring("sessionAttr:".length()), value);
}
});
return session;
}
}
由于会话数据为空(没有创建时间等),因此以下行将引发NPE:
session.setCreationTime(Instant.ofEpochMilli((Long)map.get("creationTime")));
是Bug还是我错过了Spring Boot 2.0.2+中有关Spring Session的新东西?
更新
为了提供更多信息,我创建了一个示例项目来重现该问题:https://github.com/adsanche/test-redis-session
该项目包含一个暴露两个端点的简单控制器:
@Controller
public class HelloController {
@GetMapping(value = "/hello")
public String hello(final WebSession webSession) {
webSession.getAttributes().put("test", "TEST");
return "index";
}
@GetMapping(value = "/invalidate")
public String invalidate(final WebSession webSession) {
webSession.invalidate().subscribe();
return UrlBasedViewResolver.REDIRECT_URL_PREFIX + "/hello";
}
}
使用Spring Boot 2.0.1运行项目时的行为
转到第一个端点: http://localhost:8080/hello
Redis中的会话状态:
我们注意到会话ID以7546ff开头,并且会话数据包含“ test”属性以及默认会话信息(创建/上次访问时间等)。
当前会话无效,并且在“ / hello”上执行重定向,在该重定向中,将test属性添加到新的Web会话中。
我们注意到新的会话ID以ba7de开头,并且新的会话数据仍然包含测试属性和默认会话信息。
现在,让我们在与Spring Boot 2.0.3相同的项目上重现此场景。
使用Spring Boot 2.0.3运行项目时的行为
转到第一个端点: http://localhost:8080/hello
Redis中的会话状态:
我们注意到以12d61开头的会话ID,以及包含“测试”属性和默认会话信息(创建/上次访问时间等)的会话数据。
转到第二个端点: http://localhost:8080/invalidate
Redis中新会话的状态:
我们在这里注意到仍然使用相同的会话ID,但是即使从其默认信息(创建日期等)中也清除了该会话。因此,当再次调用它时,在我在原始帖子中提到的位置的 apply()方法中的反应式redis会话存储库中会触发NPE:
希望这个示例项目可以帮助我确定这是错误还是实现方面的问题。
答案 0 :(得分:1)
这是Spring Session SpringSessionWebSessionStore
,或更具体地说是其内部WebSession
实现中的错误。问题在于,WebSession#invalidate
会话仅从基础会话存储中清除,而没有标记为无效,因此后续请求处理仍使用相同的会话ID结束。
我已经开放gh-1114来在Spring Session中解决这个问题。
我相信您之前(即春季会话2.0.2.RELEASE
和更低版本中没有遇到过这种情况,这是由于SpringSessionWebSessionStore
中已解决的2.0.3.RELEASE
中的另一个错误-请参见gh-1039。此问题与无法更新lastAccessedTime
有关,一旦我们解决了您所描述的情况,由于无效(和已删除)会话ID的会话仅使用lastAccessedTime
属性进行保存的问题,该方案开始失败。>