如何深入Tomcat源碼分析Session,針對這個問題,這篇文章詳細介紹了相對應的分析和解答,希望可以幫助更多想解決這個問題的小伙伴找到更簡單易行的方法。
創(chuàng)新互聯(lián)專業(yè)為企業(yè)提供海南網(wǎng)站建設、海南做網(wǎng)站、海南網(wǎng)站設計、海南網(wǎng)站制作等企業(yè)網(wǎng)站建設、網(wǎng)頁設計與制作、海南企業(yè)網(wǎng)站模板建站服務,10年海南做網(wǎng)站經(jīng)驗,不只是建網(wǎng)站,更提供有價值的思路和整體網(wǎng)絡服務。
Session到底是個啥?
我們都知道,HTTP協(xié)議本身是無狀態(tài)的(Stateless),這對于一些簡單的頁面展示來說,功能足夠,不受影響。而對于日漸復雜的動態(tài)頁面、應用,各種需要登錄認證等場景,就力不從心了。試想對于一個已經(jīng)登錄的用戶仍然一次次的提示登錄,會是什么感受呢?
因此,多數(shù)需要保持狀態(tài)的應用,就要保證客戶端和服務端交互狀態(tài)的一致。對于瀏覽器發(fā)起的多次請求,僅基于HTTP協(xié)議,是無法識別出是否為同一用戶請求的。而為了保持客戶端和服務端交互狀態(tài),可以采取一系列的策略,例如:
Cookie
隱藏form 表單域
Session
URL
SSL
下面將通過深入Tomcat源碼,分析這一最常用的Servlet容器內部是如何使用Session來保持客戶端與服務器狀態(tài)一致的。
做為一名Web應用開發(fā)者,我們一定都聽說過,甚至了解過Session這個詞,以及其背后代表的一些概念。
Session,中文稱之為會話,用于兩個設備之間交互時狀態(tài)的保持。因此,會話中至少要有一方需要保存會話的狀態(tài)。
在Servlet規(guī)范中,session對象由HttpSession這一接口來表示,接口描述簡明的概括了其主要作用
Provides a way to identify a user across more than one page requestor
visit to a Web site and to store information about that user.
The servlet container uses this interface to create a session between an HTTP
client and an HTTP server. The session persists for a specified time period,
across more than one connection or page request from the user. A session
usually corresponds to one user.
而Tomcat內部則是通過StandardSession實現(xiàn)了HttpSession這個接口,內部統(tǒng)一使用StandardSession來處理。
在初次請求應用時,如果需要用到Session,則會創(chuàng)建之。一般的Servlet中并不會直接使用。而如果是請求JSP文件,由于JSP默認的隱式對象中是包含
session的,其在生成Servlet文件時,內部相當于包含了
HttpServletRequest.getSession(true)
因此,請求時會直接創(chuàng)建session對象。
創(chuàng)建session的過程,大致是下面的樣子:
protected Session doGetSession(boolean create) {
// There cannot be a session if no context has been assigned yet
Context context = getContext();
if (context == null) {
return (null);
}
// Return the current session if it exists and is valid
if ((session != null) && !session.isValid()) {
session = null;
}
if (session != null) {
return (session);
}
// Return the requested session if it exists and is valid
Manager manager = context.getManager();
if (manager == null) {
return (null); // Sessions are not supported
}
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;
}
整體流程基本是先判斷是否已經(jīng)存在session,如果沒有則創(chuàng)建。如果有,則直接使用。
此時,需要注意兩個問題:
初次請求時,session如何生成并傳遞給客戶端的
后續(xù)的其它請求中,如果將客戶端的請求與服務端已有的session建立關聯(lián)的
上面代碼中,判斷session不存在并創(chuàng)建的過程,是直接調用createSession這個方法,并會根據(jù)sessionId是否為空,來確定是完全新創(chuàng)建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分鐘。
在創(chuàng)建完Session之后,Tomcat通過在響應頭中設置Set-Cookie這個MimeHeader來返回給客戶端session數(shù)據(jù)。返回的數(shù)據(jù)是這樣的:
JSESSIONID=CC4D83F3A61823AA8F980C89890A19D7; Path=/manager/; HttpOnly
設置Header的過程如下:
public void addSessionCookieInternal(final Cookie cookie) {
if (isCommitted()) { //此處判斷,如果response已經(jīng)提交,則不能再設置
return;
}
String name = cookie.getName();
final String headername = "Set-Cookie";
final String startsWith = name + "=";
String header = generateCookieString(cookie); //此處根據(jù)具體cookie的內容生成對應的串,內部會判斷cookie的版本,過期時間等
if (!set) {
addHeader(headername, header);
} }
我們看到,初次請求時,響應頭中包含了高亮的數(shù)據(jù)。
那再次請求呢,我們看到這次響應頭中沒有sessionId的數(shù)據(jù),而是轉移到請求頭中,并且是以Cookie的形式提供:
此時,傳到服務端,服務端解析Cookie對應的JSESSIOONID,并提取對應的sessionId值,與服務端對應的session數(shù)據(jù)做關聯(lián)。
我們看代碼中的實現(xiàn)
再次請求時,從Request中獲取SessionCookie的地方在這里:
CoyoteAdapter.postParseRequset()
其內部調用 parseSessionCookiesId(request), 解析請求頭中的cookie數(shù)據(jù)。
public void parseCookieHeader(MimeHeaders headers, ServerCookies serverCookies) {
// process each "cookie" header
int pos = headers.findHeader("Cookie", 0);
}
}
此處需要注意SessionCookie的名稱是允許配置的,因此這一名稱不一定一直都是JSESSIONID。
在解析Cookie獲取SessionId之后,我們拿到的僅僅是一個字符串,還不能馬上和Session關聯(lián)起來,此時request會將此值賦值給其內部的一個名為
requestSessionId的屬性。
當后面再次請求session時,就和我們最上面代碼看到的一樣,會有一個findSession的過程,
到此,我們基本了解了客戶端瀏覽器和服務端Tomcat之間,如果保持交互狀態(tài)的一致中的一種實現(xiàn)方式,即SessionCookie。
而本質上,這一過程就是傳統(tǒng)上Socket交互的一個過程,我們完全可以自已寫一段代碼模擬返回響應的數(shù)據(jù),只是需要注意響應頭數(shù)據(jù)在HTTP規(guī)范中有特定的格式要求,如下,即數(shù)據(jù)之間要以CRLF分隔
總結下,客戶端初次請求服務端會創(chuàng)建Session,此時通過在響應頭中設置Set-Cookie將sessionId傳遞給客戶端。后續(xù)客戶端的請求會在請求頭中設置Cookie項帶上sessionId,從而保證客戶端與服務端交互狀態(tài)的一致。
關于如何深入Tomcat源碼分析Session問題的解答就分享到這里了,希望以上內容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關注創(chuàng)新互聯(lián)行業(yè)資訊頻道了解更多相關知識。
當前標題:如何深入Tomcat源碼分析Session
新聞來源:http://www.chinadenli.net/article4/piihoe.html
成都網(wǎng)站建設公司_創(chuàng)新互聯(lián),為您提供關鍵詞優(yōu)化、App設計、企業(yè)建站、網(wǎng)頁設計公司、虛擬主機、服務器托管
聲明:本網(wǎng)站發(fā)布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經(jīng)允許不得轉載,或轉載時需注明來源: 創(chuàng)新互聯(lián)