我想我在spring-session
遇到了一个错误,但我想问一下这是不是一个错误。在我忘记之前
https://github.com/paranoiabla/spring-session-issue.git
这里是一个github存储库,可以重现问题。基本上我有2个控制器和2个jsps,所以流程如下:
http://localhost:8080/
,流程通过HomepageController
,它在spring会话中放置1个属性并返回homepage.jsp
,它会呈现会话ID和属性数(1 )homepage.jsp
里面有这一行:
${pageContext.include("/include")}
调用IncludeController
来调用。IncludeController
从会话存储库中找到会话并记录属性的数量(现在非常奇怪,它们被记录为0)并返回include.jsp
,它会呈现会话ID和数量会话属性(0)。
两个jsps中的会话ID都是相同的,但在pageContext.include
调用之后,某些属性被重置为空映射!
有人可以确认这是否是一个错误。谢谢。
答案 0 :(得分:1)
问题是当使用MapSessionRepository时,SessionRepositoryFilter会自动将HttpSession同步到Spring会话,后者会覆盖API的显式使用。具体而言,发生了以下情况:
我认为有两种方法可以解决这个问题。
那我该如何解决呢?最简单的方法是直接停止使用Spring Session API。这是首选,因为我们不希望在可能的情况下将自己绑定到Spring Session API。例如,而不是使用以下内容:
@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";
}
}
您可以使用以下方法简化它:
@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";
}
}
当然,如果我们无法直接使用HttpSession API,这可能会有问题。要处理此问题,您需要使用SessionRepository的不同实现。例如,另一个修复是使用RedisOperationsSessionRepository。这是有效的,因为它足够智能,只能更新已更改的属性。
这意味着在上面的步骤#3中,Redis实现只会更新上次访问的时间,因为没有更新其他属性。当IncludeController请求Spring Session时,它仍会看到在HomepageController中保存的属性。
那么为什么MapSessionRepository不这样做呢?因为MapSessionRepository基于一个全有或全无的Map。当值放在地图中时,它是单个放置(我们无法将其分解为多个操作)。