之前的时候, 有一个需求, 需要用户的 session 一直存在, 此session里面记录了一些 用户的一些业务数据, 需要 在关闭了浏览器之后, session中的数据, 依然 存在环境 : SpringSession 来托管session, 采用 redis 来存储session, 以下代码基于 spring-session - 1.2.0.RELEASE----- 手动分割线 -----然后 对于这个问题, 我想的 就是直接 session.setMaxInactiveInterval(-1), 直接 让session永不过期不就解决了吗, 但是 同事按照这种思路 实现了一下, 结果 发现 还是关闭了浏览器之后, session 就"消失"了然后 上周的时候, ??, 周二吧, 好像是, 然后 跟了一下代码, 瞅了瞅, 看了一下 HttpSession.setMaxInactiveInterval, 以及我们这里的 委托给的 ExpiringSession. setMaxInactiveIntervalInSeconds 的注释, 上面也是很明晰的写了, "A negative time indicates that the session will never timeout.", 我特么 就在想 为什么没有生效呢??上周二的时候, 浏览器里面看 SESSION 对应的 expire/Max-Age 是 session, 因此 关闭了浏览器之后, sessionId 对应的 cookie 就失效了m, 然后导致了 session 的"消失", 然后 SpringSession 重新创建了一个 session, 这就造成了我们现在的情况----- 手动分割线 -----然后 本来就是想, 那天晚上空了的时候, 看一看, 谁知道 最近妈的, 都太忙了, 加班加班加班, 然后 只好留到了周末然后 昨天的时候, 又有一些朋友的其他的事情, 然后 只好留到了今天, 然后 今天下午的时候, 又重新溜了一下 SessionRepositoryFilter 的这部分的代码然后 忽略SpringSession管理session的部分的细节, 我们这里 直奔关于我们这里的这个问题的相关部分, 为什么 sessionId 对应的 cookie 的生命周期不为很长的一个时间, 而是默认的关闭浏览器就失效呢?定位到 SessionRepositoryRequestWrapper. commitSession, 这个方法, 这个方法调用的地方是, SessionRepositoryResponseWrapper. onResponseCommitted, SessionRepositoryFilter.doFilterInternal也就是在 Response 写出数据的时候, 以及SessionRepositoryFilter 以及之后的Filter 以及XXXServlet处理完了之后的时候 调用了一下 commitSessionSessionRepositoryFilter.doFilterInternal

@Override
	protected void doFilterInternal(HttpServletRequest request,
			HttpServletResponse response, FilterChain filterChain)
					throws ServletException, IOException {
		request.setAttribute(SESSION_REPOSITORY_ATTR, this.sessionRepository);

		SessionRepositoryRequestWrapper wrappedRequest = new SessionRepositoryRequestWrapper(
				request, response, this.servletContext);
		SessionRepositoryResponseWrapper wrappedResponse = new SessionRepositoryResponseWrapper(
				wrappedRequest, response);

		HttpServletRequest strategyRequest = this.httpSessionStrategy
				.wrapRequest(wrappedRequest, wrappedResponse);
		HttpServletResponse strategyResponse = this.httpSessionStrategy
				.wrapResponse(wrappedRequest, wrappedResponse);

		try {
			filterChain.doFilter(strategyRequest, strategyResponse);
		}
		finally {
			wrappedRequest.commitSession();
		}
	}






SpringSession 管理 session 的逻辑, 就在于 SpringSession 重写的几个 Wrapper, RequestWrapper, ResponseWrapper, SessionWrapper


这里 差不多就是创建了 Wrapper, 然后把逻辑留给之后的 Filter, 以及处理业务的Servlet, 然后 责任链回到了当前 Filter, 之后 commitSession




SessionRepositoryRequestWrapper. commitSession


private void commitSession() {
			HttpSessionWrapper wrappedSession = getCurrentSession();
			if (wrappedSession == null) {
				if (isInvalidateClientSession()) {
					SessionRepositoryFilter.this.httpSessionStrategy
							.onInvalidateSession(this, this.response);
				}
			}
			else {
				S session = wrappedSession.getSession();
				SessionRepositoryFilter.this.sessionRepository.save(session);
				if (!isRequestedSessionIdValid()
						|| !session.getId().equals(getRequestedSessionId())) {
					SessionRepositoryFilter.this.httpSessionStrategy.onNewSession(session,
							this, this.response);
				}
			}
		}






这里 我们着重需要关注的是, 这里 SessionRepositoryFilter.this.httpSessionStrategy.onNewSession


this.sessionRepository.save(session) 这里, 持久化当前 session, 如果 Servlet 处理的过程中, 对于Session有一些处理, 这里外层统一处理一下, 持久化变更的数据[如果配置了 RedisHttpSessionConfiguration.flushMode为IMMEDIATE, 此配置会传到 RedisHttpSessionConfiguration. sessionRepository, 创建的 RedisOperationsSessionRepository, 然后这样, 创建的session, 每次有更新之后, 都会直接持久化到 redis中]


在客户端访问服务器第一次生成 session 的时候, 会处理一些业务, 放在这里onNewSession 




我们这里看看默认的 CookieHttpSessionStrategy 的实现


CookieHttpSessionStrategy. onNewSession



public void onNewSession(Session session, HttpServletRequest request,
			HttpServletResponse response) {
		Set<String> sessionIdsWritten = getSessionIdsWritten(request);
		if (sessionIdsWritten.contains(session.getId())) {
			return;
		}
		sessionIdsWritten.add(session.getId());

		Map<String, String> sessionIds = getSessionIds(request);
		String sessionAlias = getCurrentSessionAlias(request);
		sessionIds.put(sessionAlias, session.getId());

		String cookieValue = createSessionCookieValue(sessionIds);
		this.cookieSerializer
				.writeCookieValue(new CookieValue(request, response, cookieValue));
	}




添加 sessionKey -> sessionId, 到sessionId的集合中, 然后 通过response写出到 cookie中, 然后 这里面就是 我们要关注的重点了, 也就是 为什么我们这里的 sessionId 对应的 cookie 关闭了浏览器就失效的原因了




DefaultCookieSerializer. writeCookieValue


public void writeCookieValue(CookieValue cookieValue) {
		HttpServletRequest request = cookieValue.getRequest();
		HttpServletResponse response = cookieValue.getResponse();

		String requestedCookieValue = cookieValue.getCookieValue();
		String actualCookieValue = this.jvmRoute == null ? requestedCookieValue
				: requestedCookieValue + this.jvmRoute;

		Cookie sessionCookie = new Cookie(this.cookieName, actualCookieValue);
		sessionCookie.setSecure(isSecureCookie(request));
		sessionCookie.setPath(getCookiePath(request));
		String domainName = getDomainName(request);
		if (domainName != null) {
			sessionCookie.setDomain(domainName);
		}

		if (this.useHttpOnlyCookie) {
			sessionCookie.setHttpOnly(true);
		}

		if ("".equals(requestedCookieValue)) {
			sessionCookie.setMaxAge(0);
		}
		else {
			sessionCookie.setMaxAge(this.cookieMaxAge);
		}

		response.addCookie(sessionCookie);
	}




新建 cookie, 配置path, domain, 等等参数信息


配置 maxAge, 这里"sessionCookie.setMaxAge(this.cookieMaxAge);", 默认的 DefaultCookieStrategy. cookieMaxAge 为-1, 浏览器这边对应的就是关闭窗口 cookie 失效


然后 这里, 如果我们需要 自定义 cookie的声明周期的话, 需要 自己创建 CookieSerializer, 然后 配置下cookieMaxAge, 然后 吧刚才定义的 CookieSerializer 配置到 XXXHttpSessionConfiguration, 里面就可以了




完, 不过 溜了一圈, 里面 还是有一些疑惑的地方


spring - session
	- expirations : $timeoutTs [$timeoutTs + 300] [expires:$sessionId, ]
	- sessions
		- session : $sessionId [$timeoutTs + 300] { maxInactiveInterval : xx, lastAccessedTime : xx, creationTime : xx }
		- expires
			- $sessionId [$timeoutTs] { }




1. spring:session:sessions:expires 这一层的作用是什么??, 标记当前 有哪些存活的 session 吗 ?


2. spring:session:sessions:session:$sessionId 这一层为什么要让他多存活5分钟??, 


3. RedisOperationsSessionRepository. leanupExpiredSessions(), 这个定时任务存在的意义是什么?? 


留几个问题, 之后的时候, 再回来看看, 再来补充补充吧??