首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在pageContext.include之后,Spring-session从会话中清除属性

在pageContext.include之后,Spring-session从会话中清除属性
EN

Stack Overflow用户
提问于 2015-04-07 13:34:26
回答 1查看 1.4K关注 0票数 2

我想我在spring-session中遇到了一个bug,但我只想在这里问它是否真的是一个bug。在我忘记之前

https://github.com/paranoiabla/spring-session-issue.git

这里有一个github存储库,用于复制问题。基本上,我有2个控制器和2个jsps,所以流程如下:

  • 用户打开http://localhost:8080/,流程通过HomepageController,在春季会话中放置一个属性,并返回呈现会话id和属性数量的homepage.jsp (1)。
  • homepage.jsp中有一行:${pageContext.include("/include")},它调用要调用的IncludeController
  • IncludeController从会话存储库中查找会话,并记录属性的数量(现在它们被记录为0),并返回呈现会话id和会话属性数量(0)的include.jsp。两个jsp中的会话id是相同的,但是在pageContext.include调用之后,属性被重置为一个空映射!能不能请人确认一下这是不是个窃听器。

谢谢。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2015-04-08 15:55:44

问题

问题是,当使用MapSessionRepository时,SessionRepositoryFilter将自动将HttpSession同步到Spring会话,这会覆盖API的显式使用。具体而言,正在发生以下情况:

  1. SessionRepositoryFilter正在获取当前的Spring会话。它将其缓存在HttpServletRequest中,以确保HttpServletRequest.getSession()的每次调用都不会进行数据库调用。这个缓存版本的Spring会话没有与它相关联的属性。
  2. HomepageController获得自己的Spring会话副本,修改它,然后保存它。
  3. JSP刷新提交HttpServletResponse的响应。这意味着我们必须在设置刷新之前写出会话cookie。我们还需要确保会话在这一点上是持久化的,因为随后客户端可能会访问会话id并能够发出另一个请求。这意味着保存来自#1的Spring会话时没有任何属性可以覆盖保存在#2中的会话。
  4. IncludeController获得从#3保存的Spring会话(它没有属性)

解决方案

我认为解决这个问题有两种选择。

使用HttpSession API

那我该怎么解决这个问题。最简单的方法是停止直接使用Spring会话API。无论如何,这都是首选的,因为如果可能的话,我们不希望将自己绑定到Spring会话API。例如,不要使用以下方法:

代码语言:javascript
复制
@Controller
public class HomepageController {

    @Resource(name = "sessionRepository")
    private SessionRepository<ExpiringSession> sessionRepository;

    @Resource(name = "sessionStrategy")
    private HttpSessionStrategy sessionStrategy;

    @RequestMapping(value = "/", method = RequestMethod.GET)
    public String home(final Model model) {

        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();

        final String sessionIds = sessionStrategy.getRequestedSessionId(request);

        if (sessionIds != null) {
            final ExpiringSession session = sessionRepository.getSession(sessionIds);
            if (session != null) {
                session.setAttribute("attr", "value");
                sessionRepository.save(session);
                model.addAttribute("session", session);
            }
        }

        return "homepage";
    }

}

@Controller
public class IncludeController {

    private final static Logger LOG = LogManager.getLogger(IncludeController.class);

    @Resource(name = "sessionRepository")
    private SessionRepository<ExpiringSession> sessionRepository;

    @Resource(name = "sessionStrategy")
    private HttpSessionStrategy sessionStrategy;

    @RequestMapping(value = "/include", method = RequestMethod.GET)
    public String home(final Model model) {

        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();

        final String sessionIds = sessionStrategy.getRequestedSessionId(request);

        if (sessionIds != null) {
            final ExpiringSession session = sessionRepository.getSession(sessionIds);
            if (session != null) {
                LOG.error(session.getAttributeNames().size());
                model.addAttribute("session", session);
            }
        }

        return "include";
    }
}

您可以使用以下方法简化它:

代码语言:javascript
复制
@Controller
public class HomepageController {

    @RequestMapping(value = "/", method = RequestMethod.GET)
    public String home(HttpServletRequest request, Model model) {

        String sessionIds = request.getRequestedSessionId();

        if (sessionIds != null) {
            final HttpSession session = request.getSession(false);
            if (session != null) {
                session.setAttribute("attr", "value");
                model.addAttribute("session", session);
            }
        }

        return "homepage";
    }

}

@Controller
public class IncludeController {

    @RequestMapping(value = "/include", method = RequestMethod.GET)
    public String home(HttpServletRequest request, final Model model) {

        final String sessionIds = request.getRequestedSessionId();

        if (sessionIds != null) {
            final HttpSession session = request.getSession(false);
            if (session != null) {
                model.addAttribute("session", session);
            }
        }

        return "include";
    }
}

使用RedisOperationsSessionRepository

当然,在我们不能直接使用HttpSession API的情况下,这可能会有问题。要处理这个问题,您需要使用不同的SessionRepository实现。例如,另一个修复方法是使用RedisOperationsSessionRepository。这是因为它足够聪明,只更新已更改的属性。

这意味着在上面的步骤3中,由于没有更新其他属性,Redis实现将只更新最后一次访问时间。当IncludeController请求Spring会话时,它仍然会看到保存在HomepageController中的属性。

那么为什么MapSessionRepository不这么做呢?因为MapSessionRepository是基于地图的,这是一个全部或根本不存在的东西。当值放在映射中时,它是一个put (我们不能将其分解为多个操作)。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/29492954

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档