亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 服務器 > Web服務器 > 正文

Tomcat中實現Session小結

2024-09-01 13:50:02
字體:
來源:轉載
供稿:網友

什么是Session

對Tomcat而言,Session是一塊在服務器開辟的內存空間,其存儲結構為ConcurrentHashMap;

Session的目的

Http協議是一種無狀態協議,即每次服務端接收到客戶端的請求時,都是一個全新的請求,服務器并不知道客戶端的歷史請求記錄;

Session的主要目的就是為了彌補Http的無狀態特性。簡單的說,就是服務器可以利用session存儲客戶端在同一個會話期間的一些操作記錄;

實現機制

先看兩個問題,如下:

1、服務器如何判斷客戶端發送過來的請求是屬于同一個會話?

答:用Session id區分,Session id相同的即認為是同一個會話,在Tomcat中Session id用JSESSIONID表示;

2、服務器、客戶端如何獲取Session id?Session id在其之間是如何傳輸的呢?

答:服務器第一次接收到請求時,開辟了一塊Session空間(創建了Session對象),同時生成一個Session id,并通過響應頭的Set-Cookie:“JSESSIONID=XXXXXXX”命令,向客戶端發送要求設置cookie的響應;

客戶端收到響應后,在本機客戶端設置了一個JSESSIONID=XXXXXXX的cookie信息,該cookie的過期時間為瀏覽器會話結束;

接下來客戶端每次向同一個網站發送請求時,請求頭都會帶上該cookie信息(包含Session id);

然后,服務器通過讀取請求頭中的Cookie信息,獲取名稱為JSESSIONID的值,得到此次請求的Session id;

ps:服務器只會在客戶端第一次請求響應的時候,在響應頭上添加Set-Cookie:“JSESSIONID=XXXXXXX”信息,接下來在同一個會話的第二第三次響應頭里,是不會添加Set-Cookie:“JSESSIONID=XXXXXXX”信息的;

而客戶端是會在每次請求頭的cookie中帶上JSESSIONID信息;

舉個例子:

以chrome瀏覽器為例,訪問一個基于tomcat服務器的網站的時候,

瀏覽器第一次訪問服務器,服務器會在響應頭添加Set-Cookie:“JSESSIONID=XXXXXXX”信息,要求客戶端設置cookie,如下圖:

tomcat,session,實現

同時我們也可以在瀏覽器中找到其存儲的sessionid信息,如下圖

tomcat,session,實現

接下來,瀏覽器第二次、第三次...訪問服務器,觀察其請求頭的cookie信息,可以看到JSESSIONID信息存儲在cookie里,發送給服務器;且響應頭里沒有Set-Cookie信息,如下圖:

tomcat,session,實現

只要瀏覽器未關閉,在訪問同一個站點的時候,其請求頭Cookie中的JSESSIONID都是同一個值,被服務器認為是同一個會話。

 再舉個簡單的例子加深印象,新建個Web工程,并寫一個Servlet,在doGet中添加如下代碼,主要做如下工作

首先,從session中獲取key為count的值,累加,存入session,并打??;

然后,每次從請求中獲取打印cookie信息,從響應中獲取打印Header的Set-Cookie信息:

  /**   * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)   */  protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {        if(request.getSession().getAttribute("count") == null){      request.getSession().setAttribute("count", 0);      response.getWriter().write(0+"");    }else{      int a = Integer.parseInt(request.getSession().getAttribute("count").toString());      request.getSession().setAttribute("count", ++a);      response.getWriter().write(a+"");    }    Cookie[] cookies = request.getCookies();    StringBuffer sb = new StringBuffer();    if(cookies!=null){      for(Cookie cookie : cookies){        sb.append(cookie.getName()+":"+cookie.getValue()+",");      }      sb.deleteCharAt(sb.length()-1);    }    System.out.println("[第"+(++index)+"次訪問]from client request, cookies:" + sb);    System.out.println("[第"+(index)+"次訪問]from server response, header-Set-Cookie:" + response.getHeader("Set-Cookie"));;  }

部署到tomcat后,連續訪問該servlet,觀察控制臺輸出,如下,客戶端第一次訪問服務器的時候,在服務端的響應頭里添加了JSESSIONID信息,且接下來客戶端的每次訪問都會帶上該JSESSIONID:

tomcat,session,實現

其實這里有一個問題,session劫持

只要用戶知道JSESSIONID,該用戶就可以獲取到JSESSIONID對應的session內容,還是以上面這個例子為例,

我先用IE瀏覽器訪問該站點,比如連續訪問了5次,此時,session中的count值為:

tomcat,session,實現

查看該會話的Session id,為6A541281A79B24BC290ED3270CF15E32

tomcat,session,實現

接下來打開chrome控制臺,將IE瀏覽器獲取過來的JSESSIONID信息(“6A541281A79B24BC290ED3270CF15E32”)寫入到cookie中,如下

tomcat,session,實現

接著刪除其中的一個,只留下JSESSIONID為“6A541281A79B24BC290ED3270CF15E32”的cookie;

tomcat,session,實現

刷新頁面,發現我們從session獲取的count值已經變成6了,說明此次chrome瀏覽器的請求劫持了IE瀏覽器會話中的session,

tomcat,session,實現

Tomcat中的session實現

Tomcat中一個會話對應一個session,其實現類是StandardSession,查看源碼,可以找到一個attributes成員屬性,即存儲session的數據結構,為ConcurrentHashMap,支持高并發的HashMap實現;

  /**   * The collection of user data attributes associated with this Session.   */  protected Map<String, Object> attributes = new ConcurrentHashMap<String, Object>();

那么,tomcat中多個會話對應的session是由誰來維護的呢?ManagerBase類,查看其代碼,可以發現其有一個sessions成員屬性,存儲著各個會話的session信息:

  /**   * The set of currently active Sessions for this Manager, keyed by   * session identifier.   */  protected Map<String, Session> sessions = new ConcurrentHashMap<String, Session>();

接下來,看一下幾個重要的方法,

服務器查找Session對象的方法

客戶端每次的請求,tomcat都會在HashMap中查找對應的key為JSESSIONID的Session對象是否存在,可以查看Request的doGetSession方法源碼,如下源碼:

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 ((context != null) && (response != null) &&      context.getServletContext().getEffectiveSessionTrackingModes().          contains(SessionTrackingMode.COOKIE) &&      response.getResponse().isCommitted()) {      throw new IllegalStateException       (sm.getString("coyoteRequest.sessionCreateCommitted"));    }    // Re-use session IDs provided by the client in very limited    // circumstances.    String sessionId = getRequestedSessionId();    if (requestedSessionSSL) {      // If the session ID has been obtained from the SSL handshake then      // use it.    } else if (("/".equals(context.getSessionCookiePath())        && isRequestedSessionIdFromCookie())) {      /* This is the common(ish) use case: using the same session ID with       * multiple web applications on the same host. Typically this is       * used by Portlet implementations. It only works if sessions are       * tracked via cookies. The cookie must have a path of "/" else it       * won't be provided to for requests to all web applications.       *       * Any session ID provided by the client should be for a session       * that already exists somewhere on the host. Check if the context       * is configured for this to be confirmed.       */      if (context.getValidateClientProvidedNewSessionId()) {        boolean found = false;        for (Container container : getHost().findChildren()) {          Manager m = ((Context) container).getManager();          if (m != null) {            try {              if (m.findSession(sessionId) != null) {                found = true;                break;              }            } catch (IOException e) {              // Ignore. Problems with this manager will be              // handled elsewhere.            }          }        }        if (!found) {          sessionId = null;        }        sessionId = getRequestedSessionId();      }    } else {      sessionId = null;    }    session = manager.createSession(sessionId);    // Creating a new session cookie based on that session    if ((session != null) && (getContext() != null)        && getContext().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;  }

先看doGetSession方法中的如下代碼,這個一般是第一次訪問的情況,即創建session對象,session的創建是調用了ManagerBase的createSession方法來實現的; 另外,注意response.addSessionCookieInternal方法,該方法的功能就是上面提到的往響應頭寫入“Set-Cookie”信息;最后,還要調用session.access方法記錄下該session的最后訪問時間,因為session是可以設置過期時間的;

 session = manager.createSession(sessionId);    // Creating a new session cookie based on that session    if ((session != null) && (getContext() != null)        && getContext().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;

再看doGetSession方法中的如下代碼,這個一般是第二次以后訪問的情況,通過ManagerBase的findSession方法查找session,其實就是利用map的key從ConcurrentHashMap中拿取對應的value,這里的key即requestedSessionId,也即JSESSIONID,同時還要調用session.access方法,記錄下該session的最后訪問時間;

    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);      }    }

在session對象中查找和設置key-value的方法

這個我們一般調用getAttribute/setAttribute方法:

getAttribute方法很簡單,就是根據key從map中獲取value;

setAttribute方法稍微復雜點,除了設置key-value外,如果添加了一些事件監聽(HttpSessionAttributeListener)的話,還要通知執行,如beforeSessionAttributeReplaced, afterSessionAttributeReplaced, beforeSessionAttributeAdded、 afterSessionAttributeAdded。。。

session存在的問題

  • 安全性,session劫持,這個前面已經舉過例子了;
  • 增加服務器壓力,因為session是直接存儲在服務器的內存中的;
  • 如果存在多臺服務器的話,還存在session同步問題,當然如果只有一臺tomcat服務器的話,也就沒有session同步的事情了,然而現在一般的應用都會用到多臺tomcat服務器,通過負載均衡,同一個會話有可能會被分配到不同的tomcat服務器,因此很可能出現session不一致問題;解決session同步問題,實際上主要是保證能夠抽離出一塊共享空間存放session信息,且這塊空間不同的tomcat服務器都可以訪問到;一般這塊共享的空間可以是數據庫,或者某臺服務器的內存空間,甚至硬盤空間,或者客戶端的cookie也是可以的;

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持VEVB武林網。


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
欧美与黑人午夜性猛交久久久| 国产精品电影观看| 国产99视频在线观看| 17婷婷久久www| 国产精品爱啪在线线免费观看| 97国产成人精品视频| 亚洲а∨天堂久久精品喷水| 国产亚洲视频在线| 国产成人福利夜色影视| 国产视频自拍一区| 国产精品成人免费视频| 96精品久久久久中文字幕| 久久久噜噜噜久久久| 在线日韩欧美视频| 日本韩国在线不卡| 精品小视频在线| 日韩精品视频三区| 日韩亚洲精品电影| 亚洲女同精品视频| 欧美日韩国产二区| 性欧美暴力猛交69hd| 国产精品美女久久久久av超清| 国产精品日韩欧美大师| 一本一本久久a久久精品综合小说| 成人国产精品日本在线| 日韩中文字幕免费看| 欧洲美女7788成人免费视频| 久久亚洲精品网站| 久久精品国产96久久久香蕉| 日韩电影免费观看在线观看| 欧美噜噜久久久xxx| 国产精品日韩在线播放| 久久精品91久久香蕉加勒比| 久久影院在线观看| 欧美裸体xxxxx| 日韩视频免费观看| 97精品国产aⅴ7777| 最新亚洲国产精品| 伊是香蕉大人久久| 成人免费网视频| 国产成人一区三区| 国产免费成人av| 国产v综合v亚洲欧美久久| 大量国产精品视频| 久久综合伊人77777蜜臀| 欧美激情a∨在线视频播放| 中文字幕日韩电影| 欧美日韩成人黄色| 欧美激情成人在线视频| 国产精品久久久久久久久久99| 久久久精品免费| 国产91ⅴ在线精品免费观看| 亚洲自拍偷拍在线| 国产91热爆ts人妖在线| 欧美激情在线播放| 亚洲欧美一区二区激情| 欧美成人第一页| 国产精品美女久久久久av超清| 中文字幕精品在线视频| 精品亚洲精品福利线在观看| 国产精品久久久久久久久久东京| 国内精品小视频| 尤物yw午夜国产精品视频| 在线观看日韩av| 国产精品久久久久久av| 亚洲精品电影在线观看| 中文字幕亚洲综合久久筱田步美| 亚洲自拍小视频免费观看| 久久久成人精品视频| 国产精品网红福利| 亚洲一区二区三区777| 国产一区二区丝袜| 亚洲欧美资源在线| 韩曰欧美视频免费观看| 亚洲欧美国产精品久久久久久久| 黑人与娇小精品av专区| 久久成人这里只有精品| 久久久久久久久久久亚洲| 欧美日韩国产成人| 久久精品国产一区二区三区| 97涩涩爰在线观看亚洲| 国产精品va在线| 国产日韩欧美日韩大片| 色综合久久中文字幕综合网小说| 欧美激情视频网| 亚洲国产精久久久久久| 亚洲美女性视频| 日本精品一区二区三区在线播放视频| 亚洲国产精品va在线| 国产精品看片资源| 久久夜色精品国产| 国产精品视频久久久| 亚洲人成五月天| 久久久久久久一区二区三区| 亚洲激情在线视频| 国产91亚洲精品| www国产精品com| 社区色欧美激情 | 色综合久久88色综合天天看泰| 国产成人福利视频| 国产亚洲精品久久久优势| 69**夜色精品国产69乱| 亚洲日本中文字幕| 亚洲二区中文字幕| 亚洲二区中文字幕| 国产精品成人一区| 国产精品丝袜一区二区三区| 不卡av日日日| 77777亚洲午夜久久多人| 米奇精品一区二区三区在线观看| 久久91精品国产91久久跳| 欧美激情一级精品国产| 岛国视频午夜一区免费在线观看| 亚洲人成网站在线播| 日韩精品免费在线视频观看| 国产成人精品日本亚洲专区61| 爽爽爽爽爽爽爽成人免费观看| 在线视频日韩精品| 亚洲精品永久免费精品| 亚洲视频在线看| 亚洲男人天堂2023| 精品中文字幕在线观看| 黄色91在线观看| 国产精品丝袜白浆摸在线| 欧美丰满片xxx777| 欧美性高跟鞋xxxxhd| 欧美极品欧美精品欧美视频| 国产精品日韩在线| 欧美另类在线观看| 日韩中文有码在线视频| 91免费版网站入口| 亚洲国产欧美精品| 久久99精品久久久久久噜噜| 日本三级韩国三级久久| 亚洲xxx视频| 午夜精品三级视频福利| 日韩精品福利网站| 91精品国产色综合久久不卡98口| 亚洲性生活视频在线观看| 中文字幕精品视频| 欧美精品生活片| 国产精品视频yy9099| 久热精品在线视频| 久久久久久久久久久久av| 97精品国产91久久久久久| 成人午夜激情免费视频| 亚洲www在线观看| 国产精品视频男人的天堂| 中文字幕免费精品一区高清| 欧美电影院免费观看| 久久国产精品影片| 91免费观看网站| 久久av在线看| 日日噜噜噜夜夜爽亚洲精品| 亚洲石原莉奈一区二区在线观看| 精品国产一区二区三区久久狼黑人| 亚洲四色影视在线观看| 欧美专区在线观看| 久久这里有精品视频| 55夜色66夜色国产精品视频| 精品无人区乱码1区2区3区在线| 欧美成人性生活| 欧美疯狂性受xxxxx另类| 国内精品久久久久影院 日本资源|