如何深入Tomcat源代码分析Session,针对这个问题,本文详细介绍了相应的分析和解答,希望能帮助更多想要解决这个问题的小伙伴找到更简单更容易的方法。
会话到底是什么?
众所周知,HTTP协议本身是无状态的,对于一些简单的页面显示来说,它的功能足够强大,不会受到影响。然而,对于日益复杂的动态页面和应用,各种需要登录认证的场景都超出了我们的承受能力。想象一下,当一个登录的用户一次又一次地被提示登录时是什么感觉。
因此,对于大多数需要保持状态的应用来说,需要保证客户端和服务器端交互状态的一致性。对于浏览器发起的多个请求,仅仅基于HTTP协议是无法识别是否是来自同一个用户的请求的。为了保持客户机和服务器之间的交互状态,可以采用一系列策略,例如:
饼干
隐藏表单域
会议
统一资源定位器
加密套接字协议层
下面将分析如何在这个最常用的Servlet容器中使用Session,通过深入Tomcat源代码来保持客户机和服务器处于相同的状态。
作为一名网络应用程序开发人员,我们一定听说过甚至理解过Session这个词及其背后的一些概念。
会话,中文称为会话,用于在两个设备交互时保持状态。因此,会话中至少有一方需要保存会话状态。
在Servlet规范中,会话对象由接口HttpSession表示,接口描述简要概括了它的主要功能。
为to identify a user across more than one page request或
访问网站并存储用户信息。
servlet容器使用这个接口在HTTP
客户端和一个HTTP服务器。会话将持续指定的时间,
跨越来自用户的多个连接或页面请求。会议
通常对应一个用户。
在Tomcat中,HttpSession的接口是通过StandardSession实现的,内部使用StandardSession进行处理。
首次请求应用程序时,如果需要会话,将创建会话。它不直接用于一般的Servlet。如果是请求JSP文件,因为JSP的默认隐式对象包含
session,这相当于在生成Servlet文件时在内部包含。
httpersvletrequest . GetSession(true)
因此,当请求时,将直接创建会话对象。
创建会话的过程大致如下:
受保护的会话doGetSession(布尔创建){ 0
//如果尚未分配上下文,则不能有会话
context context=getContext();
if(context==null){ 0
返回(null);
}
//如果当前会话存在并且有效,则返回当前会话
if(((会话!=null)!session . isvalid()){ 0
session=null
}
if(会话!=null){ 0
返回(会话);
}
//如果请求的会话存在并且有效,则返回该会话
manager manager=context . getmanager();
if(manager==null){ 0
返回(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);
}
}
// Create a new session if requested and the response is not committed
if (!create) {
return (null);
}
if (response != null
&& context.getServletContext()
.getEffectiveSessionTrackingModes()
.contains(SessionTrackingMode.COOKIE)
&& response.getResponse().isCommitted()) {
throw new IllegalStateException(
sm.getString("coyoteRequest.sessionCreateCommitted"));
}
// Attempt to reuse session id if one was submitted in a cookie
// Do not reuse the session id if it is from a URL, to prevent possible
// phishing attacks
// Use the SSL session ID if one is present.
if (("/".equals(context.getSessionCookiePath())
&& isRequestedSessionIdFromCookie()) || requestedSessionSSL ) {
session = manager.createSession(getRequestedSessionId());
} else {
session = manager.createSession(null);
}
// Creating a new session cookie based on that session
if (session != null
&& context.getServletContext()
.getEffectiveSessionTrackingModes()
.contains(SessionTrackingMode.COOKIE)) {
Cookie cookie =
ApplicationSessionCookieConfig.createSessionCookie(
context, session.getIdInternal(), isSecure());
response.addSessionCookieInternal(cookie);
}
if (session == null) {
return null;
}
session.access();
return session;
}
整体流程基本是先判断是否已经存在session,如果没有则创建。如果有,则直接使用。
此时,需要注意两个问题:
-
初次请求时,session如何生成并传递给客户端的
-
后续的其它请求中,如果将客户端的请求与服务端已有的session建立关联的
上面代码中,判断session不存在并创建的过程,是直接调用createSession这个方法,并会根据sessionId是否为空,来确定是完全新创建session,还是恢复已有session。
public Session createSession(String sessionId) {
if ((maxActiveSessions >= 0) &&
(getActiveSessions() >= maxActiveSessions)) {
rejectedSessions++;
throw new TooManyActiveSessionsException(
sm.getString("managerBase.createSession.ise"),
maxActiveSessions); //注意这里有个策略
}
// Recycle or create a Session instance
Session session = createEmptySession();
// Initialize the properties of the new session and return it
session.setNew(true);
session.setValid(true);
session.setCreationTime(System.currentTimeMillis());
session.setMaxInactiveInterval(this.maxInactiveInterval);
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);
}
这里有个Session超时时间,即最大空闲时间
注意此处maxInactiveInterval的值,即为我们默认的web.xml中提供的session超时时间(后台回复关键字004,了解更多),为30分钟。
在创建完Session之后,Tomcat通过在响应头中设置Set-Cookie这个MimeHeader来返回给客户端session数据。返回的数据是这样的:
JSESSIONID=CC4D83F3A61823AA8F980C89890A19D7; Path=/manager/; HttpOnly
设置Header的过程如下:
public void addSessionCookieInternal(final Cookie cookie) {
if (isCommitted()) { //此处判断,如果response已经提交,则不能再设置
return;
}
String name = cookie.getName();
final String headername = "Set-Cookie";
final String startsWith = name + "=";
String header = generateCookieString(cookie); //此处根据具体cookie的内容生成对应的串,内部会判断cookie的版本,过期时间等
if (!set) {
addHeader(headername, header);
} }
我们看到,初次请求时,响应头中包含了高亮的数据。
那再次请求呢,我们看到这次响应头中没有sessionId的数据,而是转移到请求头中,并且是以Cookie的形式提供:
此时,传到服务端,服务端解析Cookie对应的JSESSIOONID,并提取对应的sessionId值,与服务端对应的session数据做关联。
我们看代码中的实现
再次请求时,从Request中获取SessionCookie的地方在这里:
CoyoteAdapter.postParseRequset()
其内部调用 parseSessionCookiesId(request), 解析请求头中的cookie数据。
public void parseCookieHeader(MimeHeaders headers, ServerCookies serverCookies) {
// process each "cookie" header
int pos = headers.findHeader("Cookie", 0);
}
}
此处需要注意SessionCookie的名称是允许配置的,因此这一名称不一定一直都是JSESSIONID。
在解析Cookie获取SessionId之后,我们拿到的仅仅是一个字符串,还不能马上和Session关联起来,此时request会将此值赋值给其内部的一个名为
requestSessionId的属性。
当后面再次请求session时,就和我们最上面代码看到的一样,会有一个findSession的过程,
到此,我们基本了解了客户端浏览器和服务端Tomcat之间,如果保持交互状态的一致中的一种实现方式,即SessionCookie。
而本质上,这一过程就是传统上Socket交互的一个过程,我们完全可以自已写一段代码模拟返回响应的数据,只是需要注意响应头数据在HTTP规范中有特定的格式要求,如下,即数据之间要以CRLF分隔
总结下,客户端初次请求服务端会创建Session,此时通过在响应头中设置Set-Cookie将sessionId传递给客户端。后续客户端的请求会在请求头中设置Cookie项带上sessionId,从而保证客户端与服务端交互状态的一致。
关于如何深入Tomcat源码分析Session问题的解答就分享到这里了,希望
内容来源网络,如有侵权,联系删除,本文地址:https://www.230890.com/zhan/138391.html