欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Tomcat中實(shí)現(xiàn)Session小結(jié)

 更新時(shí)間:2017年01月06日 09:55:23   作者:風(fēng)一樣的碼農(nóng)  
本篇文章主要介紹了Tomcat中實(shí)現(xiàn)Session小結(jié) ,Session的主要目的就是為了彌補(bǔ)Http的無狀態(tài)特性。簡單的說,就是服務(wù)器可以利用session存儲客戶端在同一個(gè)會(huì)話期間的一些操作記錄。

什么是Session

對Tomcat而言,Session是一塊在服務(wù)器開辟的內(nèi)存空間,其存儲結(jié)構(gòu)為ConcurrentHashMap;

Session的目的

Http協(xié)議是一種無狀態(tài)協(xié)議,即每次服務(wù)端接收到客戶端的請求時(shí),都是一個(gè)全新的請求,服務(wù)器并不知道客戶端的歷史請求記錄;

Session的主要目的就是為了彌補(bǔ)Http的無狀態(tài)特性。簡單的說,就是服務(wù)器可以利用session存儲客戶端在同一個(gè)會(huì)話期間的一些操作記錄;

實(shí)現(xiàn)機(jī)制

先看兩個(gè)問題,如下:

1、服務(wù)器如何判斷客戶端發(fā)送過來的請求是屬于同一個(gè)會(huì)話?

答:用Session id區(qū)分,Session id相同的即認(rèn)為是同一個(gè)會(huì)話,在Tomcat中Session id用JSESSIONID表示;

2、服務(wù)器、客戶端如何獲取Session id?Session id在其之間是如何傳輸?shù)哪兀?/p>

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

客戶端收到響應(yīng)后,在本機(jī)客戶端設(shè)置了一個(gè)JSESSIONID=XXXXXXX的cookie信息,該cookie的過期時(shí)間為瀏覽器會(huì)話結(jié)束;

接下來客戶端每次向同一個(gè)網(wǎng)站發(fā)送請求時(shí),請求頭都會(huì)帶上該cookie信息(包含Session id);

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

ps:服務(wù)器只會(huì)在客戶端第一次請求響應(yīng)的時(shí)候,在響應(yīng)頭上添加Set-Cookie:“JSESSIONID=XXXXXXX”信息,接下來在同一個(gè)會(huì)話的第二第三次響應(yīng)頭里,是不會(huì)添加Set-Cookie:“JSESSIONID=XXXXXXX”信息的;

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

舉個(gè)例子:

以chrome瀏覽器為例,訪問一個(gè)基于tomcat服務(wù)器的網(wǎng)站的時(shí)候,

瀏覽器第一次訪問服務(wù)器,服務(wù)器會(huì)在響應(yīng)頭添加Set-Cookie:“JSESSIONID=XXXXXXX”信息,要求客戶端設(shè)置cookie,如下圖:

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

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

只要瀏覽器未關(guān)閉,在訪問同一個(gè)站點(diǎn)的時(shí)候,其請求頭Cookie中的JSESSIONID都是同一個(gè)值,被服務(wù)器認(rèn)為是同一個(gè)會(huì)話。

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

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

然后,每次從請求中獲取打印cookie信息,從響應(yīng)中獲取打印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后,連續(xù)訪問該servlet,觀察控制臺輸出,如下,客戶端第一次訪問服務(wù)器的時(shí)候,在服務(wù)端的響應(yīng)頭里添加了JSESSIONID信息,且接下來客戶端的每次訪問都會(huì)帶上該JSESSIONID:

其實(shí)這里有一個(gè)問題,session劫持

只要用戶知道JSESSIONID,該用戶就可以獲取到JSESSIONID對應(yīng)的session內(nèi)容,還是以上面這個(gè)例子為例,

我先用IE瀏覽器訪問該站點(diǎn),比如連續(xù)訪問了5次,此時(shí),session中的count值為:

查看該會(huì)話的Session id,為6A541281A79B24BC290ED3270CF15E32

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

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

刷新頁面,發(fā)現(xiàn)我們從session獲取的count值已經(jīng)變成6了,說明此次chrome瀏覽器的請求劫持了IE瀏覽器會(huì)話中的session,

Tomcat中的session實(shí)現(xiàn)

Tomcat中一個(gè)會(huì)話對應(yīng)一個(gè)session,其實(shí)現(xiàn)類是StandardSession,查看源碼,可以找到一個(gè)attributes成員屬性,即存儲session的數(shù)據(jù)結(jié)構(gòu),為ConcurrentHashMap,支持高并發(fā)的HashMap實(shí)現(xiàn);

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

那么,tomcat中多個(gè)會(huì)話對應(yīng)的session是由誰來維護(hù)的呢?ManagerBase類,查看其代碼,可以發(fā)現(xiàn)其有一個(gè)sessions成員屬性,存儲著各個(gè)會(huì)話的session信息:

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

接下來,看一下幾個(gè)重要的方法,

服務(wù)器查找Session對象的方法

客戶端每次的請求,tomcat都會(huì)在HashMap中查找對應(yīng)的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方法中的如下代碼,這個(gè)一般是第一次訪問的情況,即創(chuàng)建session對象,session的創(chuàng)建是調(diào)用了ManagerBase的createSession方法來實(shí)現(xiàn)的; 另外,注意response.addSessionCookieInternal方法,該方法的功能就是上面提到的往響應(yīng)頭寫入“Set-Cookie”信息;最后,還要調(diào)用session.access方法記錄下該session的最后訪問時(shí)間,因?yàn)閟ession是可以設(shè)置過期時(shí)間的;

 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方法中的如下代碼,這個(gè)一般是第二次以后訪問的情況,通過ManagerBase的findSession方法查找session,其實(shí)就是利用map的key從ConcurrentHashMap中拿取對應(yīng)的value,這里的key即requestedSessionId,也即JSESSIONID,同時(shí)還要調(diào)用session.access方法,記錄下該session的最后訪問時(shí)間;

    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對象中查找和設(shè)置key-value的方法

這個(gè)我們一般調(diào)用getAttribute/setAttribute方法:

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

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

session存在的問題

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

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • 淺談Tomcat多層容器的設(shè)計(jì)

    淺談Tomcat多層容器的設(shè)計(jì)

    Tomcat的容器用來裝載Servlet。那Tomcat的Servlet容器是如何設(shè)計(jì)的呢,本文就詳細(xì)的來介紹一下,具有一定的參考價(jià)值,感興趣的可以了解一下
    2021-07-07
  • 詳解Tomcat Web 應(yīng)用綁定域名的幾種方式

    詳解Tomcat Web 應(yīng)用綁定域名的幾種方式

    本篇文章主要介紹了詳解Tomcat Web 應(yīng)用綁定域名的幾種方式,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-12-12
  • 詳解Windows下調(diào)整Tomcat啟動(dòng)參數(shù)的實(shí)現(xiàn)方法

    詳解Windows下調(diào)整Tomcat啟動(dòng)參數(shù)的實(shí)現(xiàn)方法

    這篇文章主要介紹了詳解Windows下調(diào)整Tomcat啟動(dòng)參數(shù)的實(shí)現(xiàn)方法的相關(guān)資料,希望通過本文大家能夠修改Tomcat啟動(dòng)參數(shù)來實(shí)現(xiàn)自己想要的效果,需要的朋友可以參考下
    2017-09-09
  • idea中沒有tomcat選項(xiàng)如何配置添加tomcat

    idea中沒有tomcat選項(xiàng)如何配置添加tomcat

    多的工程用IDEA打開調(diào)試,如果用到 tomcat服務(wù),都要配置一下,本文主要介紹了idea中沒有tomcat選項(xiàng)如何配置添加tomcat,下面就來具體介紹一下,感興趣的可以了解一下
    2024-05-05
  • Spring?boot整合tomcat底層原理剖析

    Spring?boot整合tomcat底層原理剖析

    SpringBoot的啟動(dòng)過程中,會(huì)調(diào)用核心的refresh方法,內(nèi)部會(huì)執(zhí)行onRefresh()方法,onRefresh()方法是一個(gè)模板方法,他會(huì)執(zhí)行會(huì)執(zhí)行子類ServletWebServerApplicationContext的onRefresh()方法,這篇文章主要介紹了Spring?boot整合tomcat底層原理,需要的朋友可以參考下
    2022-10-10
  • nginx和tomcat訪問圖片和靜態(tài)頁面的配置方法

    nginx和tomcat訪問圖片和靜態(tài)頁面的配置方法

    這篇文章主要介紹了nginx和tomcat訪問圖片和靜態(tài)頁面的配置方法,需要的朋友可以參考下
    2018-04-04
  • Linux上tomcat的虛擬主機(jī)IP映射配置(圖片服務(wù)器)

    Linux上tomcat的虛擬主機(jī)IP映射配置(圖片服務(wù)器)

    有時(shí)候我們會(huì)使用tomcat作為一個(gè)圖片資源服務(wù)器,本文主要介紹了Linux上tomcat的虛擬主機(jī)IP映射配置,通過tomcat服務(wù)器來訪問我們的圖片,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-08-08
  • 源碼剖析Tomcat類的加載原理

    源碼剖析Tomcat類的加載原理

    這篇文章主要帶大家從源碼級深入剖析一下Tomcat類的加載原理,文中的示例代碼講解詳細(xì),具有一定的學(xué)習(xí)價(jià)值,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2023-06-06
  • Linux小技巧分享之如何重新啟動(dòng)tomcat

    Linux小技巧分享之如何重新啟動(dòng)tomcat

    在Linux系統(tǒng)下,如何重啟Tomcat呢?答曰:使用命令操作的!咳咳,這不是廢話嗎。其實(shí)tomcat的安裝目錄有關(guān)閉,重啟的腳本的,只要執(zhí)行他們就可以了,下面我們來詳細(xì)說明下
    2014-08-08
  • tomcat正常啟動(dòng)但網(wǎng)頁卻無法訪問的幾種解決方法

    tomcat正常啟動(dòng)但網(wǎng)頁卻無法訪問的幾種解決方法

    本文主要介紹了tomcat正常啟動(dòng)但網(wǎng)頁卻無法訪問的幾種解決方法,根據(jù)自身使用情況和一些網(wǎng)上搜索到的結(jié)果,匯總整理一下,具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-05-05

最新評論