我想将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开始,相同的代码似乎并没有完全破坏会话 . 当前会话信息只是"cleared",之后仍然使用相同的会话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的新内容?
UPDATE
为了提供更多信息,我创建了一个重现问题的示例项目:https://github.com/adsanche/test-redis-session
该项目包含一个简单的控制器,公开两个 endpoints :
@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";
}
}
Behavior when running the project with Spring Boot 2.0.1
-
转到第一个 endpoints :http://localhost:8080/hello
-
Redis Session 现状:
我们注意到会话ID以7546ff开头,会话数据包含“test”属性加上默认会话信息(创建/上次访问时间等) .
- 转到第二个 endpoints :http://localhost:8080/invalidate
当前会话无效,并在“/ hello”上执行重定向,其中test属性添加到新的Web会话中 .
- Redis新 Session 的状况:
我们注意到以ba7de开头的新会话ID,以及仍包含测试属性和默认会话信息的新会话数据 .
现在,让我们在Spring Boot 2.0.3的同一个项目中重现这个场景 .
Behavior when running the project with Spring Boot 2.0.3
-
转到第一个终点:http://localhost:8080/hello
-
Redis Session 现状:
我们注意到会话ID以12d61开头,会话数据包含“test”属性加上默认会话信息(创建/上次访问时间等) .
-
转到第二个 endpoints :http://localhost:8080/invalidate
-
Redis新 Session 的状况:
我们在这里注意到仍然使用相同的会话ID,但是会话甚至已从其默认信息(创建日期等)中清除 . 因此,当再次调用它时,在我在原始帖子中提到的地方的apply()方法中的反应式redis会话存储库中触发NPE:
希望这个示例项目有助于找出我的错误或实现问题 .
1 回答
这是Spring Session的
SpringSessionWebSessionStore
中的错误,或者更确切地说,是其内部WebSession
实现中的错误 . 问题是WebSession#invalidate
会话仅从基础会话存储中清除,但未标记为无效,因此后续请求处理仍然使用相同的会话ID结束 .我已经打开gh-1114来在Spring Session中解决这个问题 .
我相信你之前没有经历过这种情况,即 Spring 季 Session
2.0.2.RELEASE
及以下,因为SpringSessionWebSessionStore
中的另一个错误已在2.0.3.RELEASE
中解决 - 请参阅gh-1039 . 此问题是关于无法更新lastAccessedTime
,并且一旦我们修复了您描述的方案因为无效(和已删除)会话ID的会话仅使用lastAccessedTime
属性再次保存而开始失败 .