一、概述
tomcat 识别用户,依靠的 cookie 和 session。cookie 保存在客户端,name 是 "JSESSIONID",values 是 sessionId。session 保存在 tomcat 的 StandardManager 对象中。
二、JSESSIONID 的生成与响应头
当在 servlet 中使用,request.getSession() 时,会得到 session,这个request 是 RequestFacade,会执行 Request 的 getSession(true)
1、查看 request 的成员变量 session 是否有值,如果有,直接返回 session
2、如果 request 的成员变量 session 为空,查看 request 的成员变量 requestedSessionId 是否有值,如果有从 StandardManager 获得 session (StandardManager 有 Map<String, Session> sessions
,保存的 sessionId 与 session)
3、查看 request 的 session 为 null,requestedSessionId 也为 null,使用 StandardManager 创建 StandardSesison,并放到 StandardManager 的 sessions 中;创建 cookie,cookie 的 name 是 "JSESSIONID",值是 sessionId,把 cookie 放到响应头,例:addHeader("Set-Cookie", "JSESSIONID=E5C491ACB2326D22649FCFCC994500CF; Path=/servlet_hello; HttpOnly")
三、JSESSIONID 的作用与请求头
当请求到达 tomcat,会分析请求,然后执行 filter 和 servlet
1、执行 CoyoteAdapter 的 service(request, response) 方法
2、执行 CoyoteAdapter 的 postParseRequest(req, request, res, response) 分析请求,把 name 是 JSESSIONID 的 cookie 的值,赋给 Request 的 requestedSessionId 成员变量
3、执行 AuthenticatorBase 的 invoke(request, response) 方法,request.getSessionInternal(false),
根据 requestedSessionId 获得 从 StandardManager 获得 Session,把 Session 赋值给 Request。
4、 ApplicationFilterChain 执行 doFilter(request, response) 方法,执行Filter,然后执行 Servlet
四、分析
<dependencies>
<!--
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
-->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<version>9.0.79</version>
<scope>provided</scope>
</dependency>
</dependencies>
-
第一次请求
1) 执行 NonLoginAuthenticator 的 invoke,会调用 request.getSessionInternal(false)
2) 业务执行 request.getSession(),Request 创建 session,并把 cookie 写入响应头,响应头为 Set-Cookie: JSESSIONID=E5C491ACB2326D22649FCFCC994500CF; Path=/servlet_hello; HttpOnly -
第二次请求
1) 请求携带 cookie,请求头为 Cookie: JSESSIONID=E5C491ACB2326D22649FCFCC994500CF
2) 执行 NonLoginAuthenticator 的 invoke,会调用 request.getSessionInternal(false),根据 requestedSessionId,从 StandardManager 对象中找到 session。赋值 request 中 session
3) 业务执行 request.getSession(),从 request 中得到 session
// NonLoginAuthenticator.java
@Override
public void invoke(Request request, Response response) throws IOException, ServletException {
Session session = request.getSessionInternal(false);
}
// Request.java
public Session getSessionInternal(boolean create) {
return doGetSession(create);
}
protected Session doGetSession(boolean create) {
Context context = getContext();
if (context == null) {
return null;
}
if ((session != null) && !session.isValid()) {
session = null;
}
if (session != null) {
return session;
}
Manager manager = context.getManager();
if (manager == null) {
return null;
}
if (requestedSessionId != null) {
try {
session = manager.findSession(requestedSessionId);
} catch (IOException e) {
...
session = null;
}
if ((session != null) && !session.isValid()) {
session = null;
}
if (session != null) {
session.access();
return session;
}
}
if (!create) {
return null;
}
...
String sessionId = getRequestedSessionId();
...
session = manager.createSession(sessionId);
if (session != null && trackModesIncludesCookie) {
Cookie cookie =
ApplicationSessionCookieConfig.createSessionCookie(context, session.getIdInternal(), isSecure());
response.addSessionCookieInternal(cookie);
}
if (session == null) {
return null;
}
session.access();
return session;
}
// StandardManager.java
@Override
public Session createSession(String sessionId) {
...
Session session = createEmptySession();
session.setNew(true);
session.setValid(true);
session.setCreationTime(System.currentTimeMillis());
session.setMaxInactiveInterval(getContext().getSessionTimeout() * 60);
String id = sessionId;
if (id == null) {
id = generateSessionId();
}
session.setId(id);
sessionCounter++;
SessionTiming timing = new SessionTiming(session.getCreationTime(), 0);
synchronized (sessionCreationTiming) {
sessionCreationTiming.add(timing);
sessionCreationTiming.poll();
}
return session;
}
@Override
public Session createEmptySession() {
return getNewSession();
}
protected StandardSession getNewSession() {
return new StandardSession(this);
}
StandardSession 执行 setId() 方法时,会调用事件通知
// StandardSession.java
@Override
public void setId(String id) {
setId(id, true);
}
@Override
public void setId(String id, boolean notify) {
if ((this.id != null) && (manager != null)) {
manager.remove(this);
}
this.id = id;
if (manager != null) {
manager.add(this);
}
if (notify) {
tellNew();
}
}
public void tellNew() {
fireSessionEvent(Session.SESSION_CREATED_EVENT, null);
Context context = manager.getContext();
Object listeners[] = context.getApplicationLifecycleListeners();
if (listeners != null && listeners.length > 0) {
HttpSessionEvent event = new HttpSessionEvent(getSession());
for (Object o : listeners) {
if (!(o instanceof HttpSessionListener)) {
continue;
}
HttpSessionListener listener = (HttpSessionListener) o;
try {
context.fireContainerEvent("beforeSessionCreated", listener);
listener.sessionCreated(event);
context.fireContainerEvent("afterSessionCreated", listener);
} catch (Throwable t) {
...
context.fireContainerEvent("afterSessionCreated", listener);
...
}
}
}
}