SpringSession源码分析
默认对常规Session的理解和使用,如何使用Set-Cookie。
Maven库
常见的spring-session-data-redis
依赖spring-session-core
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-core</artifactId>
</dependency>
带着问题看源码
1、Controller
代码块内request.getSession()
是在哪创建的呢?
2、响应头Set-Cookie
是在什么地方赋值的呢?
SessionRepositoryFilter 拦截器
org.springframework.session.web.http.SessionRepositoryFilter
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
request.setAttribute(SESSION_REPOSITORY_ATTR, this.sessionRepository);
// 请求包装,进入包装类可以看到request.getSession()源码
SessionRepositoryFilter<S>.SessionRepositoryRequestWrapper wrappedRequest = new SessionRepositoryRequestWrapper(request, response);
SessionRepositoryFilter<S>.SessionRepositoryResponseWrapper wrappedResponse = new SessionRepositoryResponseWrapper(wrappedRequest, response);
try {
filterChain.doFilter(wrappedRequest, wrappedResponse);
} finally {
// 响应头`Set-Cookie`赋值入口(这里不理解为啥使用请求提交,而不是用响应提交`wrappedResponse.commitSession()`)
// 方法跳转到DefaultCookieSerializer类就看见具体Set-Cookie了
wrappedRequest.commitSession();
}
}
SessionRepositoryRequestWrapper 包装类
org.springframework.session.web.http.SessionRepositoryFilter.SessionRepositoryRequestWrapper
Spring中存在很多继承HttpServletRequestWrapper的包装类
public SessionRepositoryFilter<S>..SessionRepositoryRequestWrapper.HttpSessionWrapper getSession(boolean create) {
SessionRepositoryFilter<S>..SessionRepositoryRequestWrapper.HttpSessionWrapper currentSession = this.getCurrentSession();
if (currentSession != null) {
return currentSession;
} else {
S requestedSession = this.getRequestedSession();
if (requestedSession != null) {
if (this.getAttribute(SessionRepositoryFilter.INVALID_SESSION_ID_ATTR) == null) {
requestedSession.setLastAccessedTime(Instant.now());
this.requestedSessionIdValid = true;
currentSession = new HttpSessionWrapper(requestedSession, this.getServletContext());
currentSession.markNotNew();
this.setCurrentSession(currentSession);
return currentSession;
}
} else {
if (SessionRepositoryFilter.SESSION_LOGGER.isDebugEnabled()) {
SessionRepositoryFilter.SESSION_LOGGER.debug("No session found by id: Caching result for getSession(false) for this HttpServletRequest.");
}
this.setAttribute(SessionRepositoryFilter.INVALID_SESSION_ID_ATTR, "true");
}
if (!create) {
return null;
} else if (SessionRepositoryFilter.this.httpSessionIdResolver instanceof CookieHttpSessionIdResolver && this.response.isCommitted()) {
throw new IllegalStateException("Cannot create a session after the response has been committed");
} else {
if (SessionRepositoryFilter.SESSION_LOGGER.isDebugEnabled()) {
SessionRepositoryFilter.SESSION_LOGGER.debug("A new session was created. To help you troubleshoot where the session was created we provided a StackTrace (this is not an error). You can prevent this from appearing by disabling DEBUG logging for " + SessionRepositoryFilter.SESSION_LOGGER_NAME, new RuntimeException("For debugging purposes only (not an error)"));
}
S session = SessionRepositoryFilter.this.sessionRepository.createSession();
session.setLastAccessedTime(Instant.now());
currentSession = new HttpSessionWrapper(session, this.getServletContext());
this.setCurrentSession(currentSession);
return currentSession;
}
}
}
// 看这里
@Override
public SessionRepositoryFilter<S>..SessionRepositoryRequestWrapper.HttpSessionWrapper getSession() {
return this.getSession(true);
}
DefaultCookieSerializer
org.springframework.session.web.http.DefaultCookieSerializer
特别注意地方是:Set-Cookie内值是通过Base64加密的。
public void writeCookieValue(CookieSerializer.CookieValue cookieValue) {
HttpServletRequest request = cookieValue.getRequest();
HttpServletResponse response = cookieValue.getResponse();
StringBuilder sb = new StringBuilder();
sb.append(this.cookieName).append('=');
String value = this.getValue(cookieValue);
if (value != null && value.length() > 0) {
this.validateValue(value);
sb.append(value);
}
int maxAge = this.getMaxAge(cookieValue);
if (maxAge > -1) {
sb.append("; Max-Age=").append(cookieValue.getCookieMaxAge());
ZonedDateTime expires = maxAge != 0 ? ZonedDateTime.now(this.clock).plusSeconds((long)maxAge) : Instant.EPOCH.atZone(ZoneOffset.UTC);
sb.append("; Expires=").append(expires.format(DateTimeFormatter.RFC_1123_DATE_TIME));
}
String domain = this.getDomainName(request);
if (domain != null && domain.length() > 0) {
this.validateDomain(domain);
sb.append("; Domain=").append(domain);
}
String path = this.getCookiePath(request);
if (path != null && path.length() > 0) {
this.validatePath(path);
sb.append("; Path=").append(path);
}
if (this.isSecureCookie(request)) {
sb.append("; Secure");
}
if (this.useHttpOnlyCookie) {
sb.append("; HttpOnly");
}
if (this.sameSite != null) {
sb.append("; SameSite=").append(this.sameSite);
}
response.addHeader("Set-Cookie", sb.toString());
}
🔚
源码分析主要是找到入口,关键入口代码已经贴上了,剩下的自己打断点看吧。