【问题标题】:Websession invalidation not working from Spring Boot 2.0.2Websession 失效在 Spring Boot 2.0.2 中不起作用
【发布时间】:2018-12-14 05:07:34
【问题描述】:

我想将我的 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")));

这是一个错误还是我错过了 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 运行项目时的行为

我们注意到会话 ID 以 7546ff 开头,会话数据包含“test”属性以及默认会话信息(创建/上次访问时间等)。

当前会话失效,并在“/hello”上执行重定向,其中test属性被添加到新的Web会话中。

  • Redis 中新会话的状态:

我们注意到以 ba7de 开头的新会话 ID,新会话数据仍包含测试属性和默认会话信息。

现在,让我们用 Spring Boot 2.0.3 在同一个项目上重现这个场景。

使用 Spring Boot 2.0.3 运行项目时的行为

我们注意到会话 ID 以 12d61 开头,会话数据包含“test”属性以及默认会话信息(创建/上次访问时间等)。

我们注意到这里仍然使用相同的会话 ID,但会话已从其默认信息(创建日期等)中清除。因此,当再次调用它时,会在我在原始帖子中提到的位置的 apply() 方法中的反应式 redis 会话存储库中触发 NPE:

希望这个示例项目有助于找出我这边的错误或实现问题。

【问题讨论】:

  • 您能否分享一段代码,显示您在何处/在何种情况下使会话无效并解释您要实现的目标?
  • 我基本上想注销一个用户(不涉及 Spring Security,因为我的登录功能只是应用页面内容自定义)。我有一个注销端点,它使会话无效 (webSession.invalidate().subscribe()) 并在主页上执行重定向,但是当再次调用会话时,它仍然是返回的旧的和无效的。我在原始帖子中提供了一个示例项目 (github.com/adsanche/test-redis-session) 和一个场景来说明我的问题。

标签: java spring spring-boot spring-webflux spring-session


【解决方案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 属性再次保存。

【讨论】:

  • 感谢您的回复,很高兴听到这是一个已知问题。目前我会坚持使用 Spring Boot 2.0.1,希望在未来的版本中尽快解决。
  • @Sancho 我已经在master 中修复了gh-1114。如果您可以尝试2.0.5.BUILD-SNAPSHOT 并确认它可以解决您的问题,那就太好了。
  • 我尝试使用 2.0.5-BUILD-SNAPSHOT 重载 spring-session-core 依赖项,它解决了问题。使会话无效时,随后会创建一个新的会话 ID,并用默认会话数据(创建时间等)填充新会话。迫不及待地想在下一个版本中修复。感谢您的快速行动和您在框架上的工作。
猜你喜欢
  • 2019-03-16
  • 2018-11-14
  • 2018-11-20
  • 1970-01-01
  • 2018-10-25
  • 2018-09-24
  • 2020-10-31
  • 2019-11-25
  • 2015-04-02
相关资源
最近更新 更多