一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - 淺談cookie和session(小結)

淺談cookie和session(小結)

2021-06-14 14:14xdx Java教程

這篇文章主要介紹了淺談cookie和session(小結),cookie和session在java web開發中扮演了十分重要的作用,本篇文章對其中的重要知識點做一些探究和總結

cookie和session在java web開發中扮演了十分重要的作用,本篇文章對其中的重要知識點做一些探究和總結。

1.cookie存在于瀏覽器

隨意打開一個網址,用火狐的調試工具,隨意選取一個鏈接,查看其請求頭。你就會看到cookie的信息。如下圖所示。

淺談cookie和session(小結)

如上圖所示,我們訪問了新浪網,通過火狐瀏覽器的調試窗口可以看到cookie存在于請求頭也就是httprequest中,并且是以鍵值對(數組)的形式存在。

只要有請求,就會在請求頭攜帶一個cookie的數組(鍵值對)。cookie是瀏覽器層面的東西。

2.java中獲取cookie

事實上,在java的servlet體系里,我們可以通過如下方式獲取cookie.

?
1
2
3
4
5
6
httpservletrequest req=servletactioncontext.getrequest();
    cookie[] cookies=req.getcookies();
    for(int i=0;i<cookies.length;i++){
      cookie cookie=cookies[i];
      system.out.println("name:"+cookie.getname()+",domain"+cookie.getdomain()+",value:"+cookie.getvalue()+",maxage:"+cookie.getmaxage());
    }

可以看到,在servlet體系中,把cookie作為一個屬性放到了httprequest對象里面。通過getcookies()方法得到一個cookie數組。

我們在一個action中加入上述代碼,并且訪問這個action,則可以看到控制臺打印出如下信息。

淺談cookie和session(小結)

servlet對cookie進行了封裝,cookie對象有幾個屬性,如name,domain,value,maxage等,具體的意義可以參考servlet的api文檔。

以上的請求的cookie是我首次訪問某一個網站的鏈接時候產生的。可以看到cookie數組中只有一個元素。這邊先注意一下,后續會有更進一步的說明。

3.java中向cookie中添加元素

說了獲取cookie數組和cookie,我們一定也想知道如何把我們自己的一些信息放進cookie,其實很簡單。http的一次請求總是伴隨著一次響應,我們就將cookie信息放入到響應中,傳遞給瀏覽器。在java下代碼是這樣寫的。

?
1
2
3
httpservletresponse res=servletactioncontext.getresponse();
    cookie cookie=new cookie("xdx", "i'm xdx");
    res.addcookie(cookie);

可以看到當我們發起這個請求時,在響應頭有下列信息。

淺談cookie和session(小結)

也就是通過這次請求,我們把xdx=i'm xdx 這個cookie通過response放進了瀏覽器。

當我們再次訪問該網站上的其他頁面的時候,在請求頭都將帶有這個cookie。如下圖所示。

淺談cookie和session(小結)

而假如我們清除歷史記錄,包括cookie。

淺談cookie和session(小結)

再次訪問該網站的某一個地址。剛才我們加進去的cookie就不存在了。

淺談cookie和session(小結)

總結來說就是:servlet通過response將cookie放入到cookie數組中,這樣瀏覽器端就會擁有這一個cookie信息。瀏覽器會在以后的請求過程中把這個cookie信息放在請求頭。

4.session是什么

session我們一般指的是httpsession,為了理解它,我們直接打開httpsession的源碼來一看究竟。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
/**
 *
 * provides a way to identify a user across more than one page
 * request or visit to a web site and to store information about that user.
 *
 * <p>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, who may visit a site many times. the server can maintain a
 * session in many ways such as using cookies or rewriting urls.
 *
 * <p>this interface allows servlets to
 * <ul>
 * <li>view and manipulate information about a session, such as
 *   the session identifier, creation time, and last accessed time
 * <li>bind objects to sessions, allowing user information to persist
 *   across multiple user connections
 * </ul>
 *
 * <p>when an application stores an object in or removes an object from a
 * session, the session checks whether the object implements
 * {@link httpsessionbindinglistener}. if it does,
 * the servlet notifies the object that it has been bound to or unbound
 * from the session. notifications are sent after the binding methods complete.
 * for session that are invalidated or expire, notifications are sent after
 * the session has been invalidated or expired.
 *
 * <p> when container migrates a session between vms in a distributed container
 * setting, all session attributes implementing the {@link httpsessionactivationlistener}
 * interface are notified.
 *
 * <p>a servlet should be able to handle cases in which
 * the client does not choose to join a session, such as when cookies are
 * intentionally turned off. until the client joins the session,
 * <code>isnew</code> returns <code>true</code>. if the client chooses
 * not to join
 * the session, <code>getsession</code> will return a different session
 * on each request, and <code>isnew</code> will always return
 * <code>true</code>.
 *
 * <p>session information is scoped only to the current web application
 * (<code>servletcontext</code>), so information stored in one context
 * will not be directly visible in another.
 *
 * @author  various
 *
 * @see   httpsessionbindinglistener
 * @see   httpsessioncontext

簡單的翻譯一下:

--提供一個在多頁面請求切換的情況下用于驗證、存儲用戶信息的手段。

--servlet容器使用httpsession來創建連接客戶端和服務端的一個會話(session)。這個會話能持續一段指定的時間(也就是我們常說的session過期時間),該會話能在多個請求之間共享。

--這個會話一般跟用戶信息關聯,由于這個用戶可能多次訪問網站,所以我們把他們存儲在這個會話(也就是httpsession)里。

--服務端通常是通過cookies或者rewriting urls來保持一個session。

我的理解,session是一種持久的會話,它的存在主要是為了克服http無狀態的特點,關于http無狀態,或者說沒有記憶,這里不多闡述,涉及到計算機網絡的知識,簡單來說就是http一次請求對應一次響應,在這個過程中會攜帶一些信息,但這些信息也僅僅在這個過程中有效。當一個請求結束,我們進入下一個請求的時候,上一個請求里面的信息對當前的請求就沒什么意義了,因為當前的請求根本不會知道上一個請求里面所包含的信息。

那么當我們需要一些在各個請求都能公用的信息的時候,該怎么辦呢?有很多辦法,可以把信息存在數據庫,然后每次從數據庫去取出來,當然io存取會浪費很多時間,它僅僅針對大數據量。還有一種就是將這些信息存在內存當中。沒錯session其實就是這樣一種對象,他把項目當中一些常用的信息存在內存當中,這些常用的信息通常是跟用戶相關的,比如用戶名,用戶昵稱,用戶角色等。因為他們需要經常用到,所以把這些信息存在session中進行管理再好不過了。

5.如何獲取一個session

在servlet體系里,我們可以用如下代碼來獲取session。

?
1
2
3
httpservletrequest request = servletactioncontext.getrequest();
    httpsession httpsession=request.getsession();
    system.out.println(httpsession);

我們來查閱httpservletrequest的源碼,看看其getsession()方法。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
/**
   *
   * returns the current session associated with this request,
   * or if the request does not have a session, creates one.
   *
   * @return    the <code>httpsession</code> associated
   *      with this request
   *
   * @see  #getsession(boolean)
   *
   */
 
  public httpsession getsession();

它的解釋是返回當前與request關聯的session,如果這個請求不存在session,就新建一個。

我們在兩個請求中加入上述代碼并運行,得到如下結果。

淺談cookie和session(小結)

 

可以看到在整個項目內,這個session被共享著調用。

6.session和cookie到底有什么關系呢?

簡單點說,每一個session對象都有一個sessionid,而在cookie數組中,又一個元素叫做jsessionid,服務器將session對象的sessionid,以名稱叫做jsessionid,值為sessionid的形式存入cookie數組中,這樣cookie和session就發生了關聯。

上面的描述可以用下圖來表示。

淺談cookie和session(小結)

上述的過程可以用類似如下的代碼來實現。

?
1
2
3
4
httpservletrequest req=servletactioncontext.getrequest();
    httpservletresponse res=servletactioncontext.getresponse();
    cookie cookie=new cookie("jsessionid", req.getsession().getid());
     res.addcookie(cookie);

只不過我們并不需要寫這個代碼,servlet自動幫我們完成了如上的操作。

7.具體過程

具體的過程是這樣的:

(1)當我們首次在某個請求中通過調用request.getsession去獲取session的時候(這個調用不一定是顯式的,很多框架把session封裝成map等其他的類型,名稱也不一定是session,但是本質都是在調用session),首先servlet通過getcookie的到本次請求的cookie信息,然后去尋找cookie數組中是否有否有一個名為jsessionid的cookie,沒有的話就創建一個session,并且把sessionid做為jsessionid這個cookie的值,然后調用addcookie()方法把該cookie放入cookie數組。

(2)如果上一步中從cookie數組中取到的cookie數組已經包含了jsessionid這個cookie,這時候我我們取出jsessionid的值,然后去內存中的session(內存中有很多session)去尋找對應的sessionid為jsessionid的值的session,如果找得到的話,就使用這個session,找不到的話,就新建一個session,并且同樣的調用addcookie()方法覆蓋掉原來的jsessionid這個cookie的值。

上述的過程可以用類似如下的代碼來表示。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
httpservletrequest req=servletactioncontext.getrequest();//具體獲取request的情況可能有所不同
  httpsession session;
    cookie jsessionid=null;
    cookie[] cookies=req.getcookies();
    for(int i=0;i<cookies.length;i++){
      cookie cookie=cookies[i];
      if(cookie.getname().equals("jsessionid")){
        jsessionid=cookie;
      }
    }
    if(jsessionid==null){
      session= createsession();//創建一個session
    }else{
     session=findsessionbysessionid(jsessionid.getvalue());//通過sessionid獲取session
      if(session==null){
        session= createsession();//創建一個session
      }
      httpservletrequest req=servletactioncontext.getresponse();//具體情況可能有所不同
      cookie cookie=new cookie("jsessionid", session.getid());
      res.addcookie(cookie);

我們將瀏覽器緩存清除,這樣cookie中就沒有jsessionid了,然后我們訪問一個action。如下。

8.當我們清空瀏覽器的時候,session會消失嗎?

這個問題包含著一些陷阱。因為很多時候當我們清空瀏覽器以后,確實需要重新登錄系統才可以操作,所以很多人自然而然認為清空瀏覽器緩存(包含cookie)以后。session就會消失。

其實這種結論是錯誤的。要知道,session是存在于服務器的,你清除瀏覽器緩存,只是清除了cookie,跟session一點關系都沒有。那么為什么我們卻不能訪問網站,而需要重新登錄了呢?

一般的web項目會通過session來判斷用戶是否有登錄,常用的判斷語句是if(session.get("userid")==null。如果為空,則表示需要重新登錄。這時候其實隱式地調用了getsession()方法,這就回到了上一步我們所講的session獲取的具體步驟了。

因為清空了瀏覽器緩存,這時候cookie數組中必定不會有jsessionid這個cookie,所以必須得新建一個session,用新的sessionid來給jsessionid這個cookie賦值。由于是新建的session,session中必定沒有userid這樣的屬性值,所以判斷結果自然為空,所以需要重新登錄。這次賦值以后,下一次再請求該網站的時候,由于cookie數組中已經有了jsessionid這個cookie,并且能通過該jsessionid的值找到相應的session,所以就不需要再重新登錄了。

9.討論幾種極端情況

第一種:當項目正常運行,我們清空瀏覽器,cookie和session會發生什么變化。

因為清空了瀏覽器,所以不會存在jsessionid這個cookie,servlet無法找到對應的session,所以他會新建一個session,然后在本次請求的響應中將sessionid傳入cookie中。

下一次請求,cookie數組中就帶有jsessionid這個cookie了,servlet就可以找到對應的session,沿用即可。

第二種:瀏覽器正常,項目重啟(可停止tomcat來模擬這種情況),cookie和session會發生什么變化。

因為項目重啟,內存中的一切session都消失了,雖然訪問一個action,請求頭中有jsessionid這個cookie,但是通過它的值(sessionid)并不能找到session(因為根本沒有任何session),所以還是得重新創建一個session,并且這個session的sessionid跟當前cookie數組中的jsessionid的值不一樣,所以它會將新的sessionid覆蓋掉cookie數組中原來的jsessionid,并且由于此時的session是嶄新的,所以他不可能有userid這樣的屬性值,所以在攔截的時候依然會被截獲,因此也是需要重新登錄的。

有興趣的同學可以去試驗一下。

10.session的時限

可以通過setmaxinactiveinterval()方法來設置session的時限,比如可以設為半個小時。這個時間指的是session不活躍開始計算的時間。超過這個時間,session就失效了。此時若再getsession(),則會創建一個新的session,并且其sessionid為此時瀏覽器中jsessionid的值。

有三種方式來設置session的時限:

--通過在web容器中設置(以tomcat為例),在tomcat-7.0\conf\web.xml中設置,以下是tomcat7.0中默認配置:

?
1
2
3
<session-config>
<session-timeout>30</session-timeout>
</session-config>

--通過web項目的web.xml文件來設置。設置15分鐘失效。

?
1
2
3
<session-config>
<session-timeout>15</session-timeout>
</session-config>

--直接在代碼中設置

?
1
session.setmaxinactiveinterval(30*60);//以秒為單位,即在沒有活動30分鐘后,session將失效

上述三種方法的優先級:1<2<3.

我們來做個試驗,在web.xml中設置session的過期時間為1分鐘,然后觀察session和cookie的變化。

第一次訪問:

淺談cookie和session(小結)

過了一分鐘后,再次訪問

淺談cookie和session(小結)

發現還是沒變,但馬上再次訪問。

淺談cookie和session(小結)

結合我們之前所說的session和cookie的作用過程來解釋一下:第二次訪問的時候,因為過了一分鐘,超過了session的過期時間,所以此時雖然cookie中還是原來的sessionid(這也是為什么第二次與第一次的請求頭中sessionid相同的原因),但是通過此sessionid是無法再找到那個已經失效的session,所以服務端必須重新創建一個session,并且把新的sessionid放到cookie中,覆蓋掉原來舊的。所以當我們馬上再次訪問的時候,這一次就是新的cookie了。

ps:其實這個過程跟服務端暫停服務的效果是一樣的,只不過服務端暫停服務影響的是內存中的所有session,而session過期只是影響當前過期的這個session。

pss:cookie過期與session過期類似,只不過是發生在客戶端,大家可以依照前面講的作用過程試著推導cookie過期會發生什么事情。

psss:如果用了redis等內存數據庫來管理session,那么設置過期時間將不起作用。

12.非瀏覽器平臺使用session

cookie依賴于瀏覽器,session依賴于服務器。如果項目不在瀏覽器上面運行,那么cookie也就無用武之地,但是我們還是想要使用session。不巧的是,session依賴cookie進行管理,這時候要怎么辦呢?

舉個很常見的場景,我們想要在安卓,或者ios、或者微信小程序等非瀏覽器的項目中使用session。這時候這些客戶端不會自動幫我們做管理cookie的工作。那么這時候我們需要自己來做。

前面講到過,servlet底層幫我們自動使用cookie來管理session,大體過程是:從cookie中找尋sessionid,根據sessionid去找session,找到合適的就用,找不到的話就新建一個,并且用新的sessionid覆蓋掉cookie中舊的。

而現在需要明白兩點:

(1)我們沒有瀏覽器了,所以不會在http的請求頭中攜帶cookie信息,servlet后臺收不到cookie信息,自然找不到jessionid,所以他會在每次請求都創建一個新的session,這對于服務端來說,是一筆嚴重的性能開銷。

(2)因為沒有瀏覽器了,servlet在更新完sessionid以后,不會自動執行set-cookie操作將新的sessionid去覆蓋舊的cookie中的jessionid的值。

根據以上兩點,我我們需要做如下幾件事情。

(1):模擬http請求的時候在請求頭中帶上cookie,在cookie中塞入從jessionid。這樣服務端就可以取到jessionid,從而獲取sessionid,進行比對來獲取session;

(2),在響應給客戶端的時候,手動調用set-cookie方法將本次的sessionid放入jessionid中,這樣客戶端就可以得到最新的jessionid。

(3):然后客戶端需要維護一個cookie的靜態變量(或者用其他方法,總之就是維護一個cookie的內存變量)。將服務端響應回來的jessionid的值存在這個變量里面。作為下次請求的時候放入請求頭。

(4):我們同樣可以在服務端寫一個攔截器,客戶端的每個請求都必須先經過攔截器,這樣就可以通過session來判斷用戶是否處于登錄狀態了。這個過程與在瀏覽器平臺毫無二致。

下面我們來實現上面的功能。

由于本人不會寫安卓和ios的代碼,所以以一個客戶端程序來模擬安卓端。

首先,定義一個靜態變量用于存儲jessionid.初始值為空。

?
1
public static string j_sessionid="";

接下來我們編寫模擬http請求的類,需要在這個類中的請求頭中加入cookie,并且在響應的時候得到響應頭中的cookie.具體代碼如下。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
public static string http4cookie(string url, string param,string jsessionid) {
    string result = "";
    bufferedreader in = null;
    try {
      string urlnamestring = url + "?" + param;
      url realurl = new url(urlnamestring);
      // 打開和url之間的連接
      httpurlconnection connection = (httpurlconnection) realurl
          .openconnection();
      // 設置通用的請求屬性
      connection.setrequestproperty("accept", "*/*");
      connection.setrequestproperty("connection", "keep-alive");
      connection.setrequestproperty("user-agent",
          "mozilla/4.0 (compatible; msie 6.0; windows nt 5.1;sv1)");
      connection.setrequestproperty("cookie", "jsessionid="+jsessionid);//請求頭中加入jessionid
      // 建立實際的連接
      connection.connect();
      // 獲取所有響應頭字段
      map<string, list<string>> map = connection.getheaderfields();
      // 遍歷所有的響應頭字段,獲取響應頭中的jessionid信息
       for (string key : map.keyset()) {
//         system.out.println(key + "--->" + map.get(key));
         if("set-cookie".equals(key)){
           string setcookie=map.get(key).tostring();
           string newjessionid=setcookie.substring(12,setcookie.lastindexof("]"));
           j_sessionid=newjessionid;// 維護新的j_sessionid
           system.out.println(setcookie.substring(12,setcookie.lastindexof("]")));
         }
       }
      // 定義 bufferedreader輸入流來讀取url的響應
      in = new bufferedreader(new inputstreamreader(
          connection.getinputstream(), "utf-8"));
      string line;
      while ((line = in.readline()) != null) {
        result += line;
      }
    } catch (exception e) {
      system.out.println("發送get請求出現異常!" + e);
      e.printstacktrace();
    }
    // 使用finally塊來關閉輸入流
    finally {
      try {
        if (in != null) {
          in.close();
        }
      } catch (exception e2) {
        e2.printstacktrace();
      }
    }
    return result;
  }

然后我們在服務端寫一個action讓客戶端來模擬請求。

?
1
2
3
4
5
6
7
8
9
10
11
@responsebody
  @requestmapping("httpcookietest")
  public string httptest(httpservletrequest req,httpservletresponse res) {
    cookie[]cookies=req.getcookies();
    for(cookie cookie:cookies){
      if(cookie.getname().equals("jsessionid"));
      system.out.println(cookie.getvalue());
    }
    outputmsg.outputmsg(res, req, "httpcookietest");
    return null;
  }

特別注意這里的outputmsg,它的代碼如下。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public static void outputmsg(httpservletresponse response,httpservletrequest request,string msg) {
    response.setcharacterencoding("utf-8");
    string sessionid = request.getsession().getid();
    response.setcontenttype("text/html; charset=utf-8");
    response.setheader("set-cookie", "jsessionid=" + sessionid);
    printwriter writer = null;
    try {
      writer = response.getwriter();
      writer.print(msg);
      writer.flush();
    } catch (ioexception e) {
      e.printstacktrace();
    } finally {
      if (writer != null)
        writer.close();
    }
  }

注意到第4和第5行代碼,這兩行代碼往響應頭中加入了此時的sessionid,作為jsessionid的值,放入到cookie中。這樣客戶端才能從響應頭中獲取最新的jsessionid,以更新靜態變量j_sessionid的值,對應http4cookie中的代碼。

然后我們來看看客戶端的主函數。

?
1
2
3
public static void main(string args[]){
    httputil.http4cookie("http://192.168.1.185:8080/warrior/httpcookietest","1=1",j_sessionid);
  }

這樣以后,其實這個過程已經跟瀏覽器的cookie運作機制并無二致了。

最后,我們在寫一個攔截器,對所有客戶端請求進行攔截。客戶端每次請求之間都率先訪問這個攔截器。

?
1
2
3
4
5
6
7
8
9
@responsebody
  @requestmapping("otherplatformintercept")
  public string otherplatformintercept(httpservletrequest req){
    httpsession httpsession=req.getsession();
    if(httpsession.getattribute("userid")!=null){
      return "valid";
    }
    return "invalid";
  }

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。

原文鏈接:http://www.xdxxdxxdx.com/article/31

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 亚洲国产日韩欧美一区二区三区 | 国产一级一级一级成人毛片 | 亚洲精品资源 | 国产午夜亚洲精品理论片不卡 | 亚洲天天做夜夜做天天欢 | 国产一精品一av一免费爽爽 | 99自拍视频在线观看 | 国产拍拍拍 | 美女无遮挡 | 日本免费三片在线播放 | 果冻传媒九一制片厂 | hd在线观看免费高清视频 | 网红思瑞一区二区三区 | 天天色综合三 | 9191视频| 91久久青青青国产免费 | 天天干夜夜噜 | 无码中文字幕av免费放 | 日韩欧免费一区二区三区 | 欧美人禽杂交狂配无删完整 | 亚洲高清成人 | 国产网站免费观看 | 青青草在视线频久久 | 亚洲欧美日韩一区成人 | 亚洲a视频在线观看 | 日本草草视频在线观看 | 好大好爽好涨太深了小喜 | 极品丝袜老师h系列全文阅读 | 日本免费三片在线观看 | 污小说| 亚洲第一福利视频 | 成年视频在线观看免费 | 国产精品免费综合一区视频 | 日本无遮挡吸乳视频看看 | 久草热8精品视频在线观看 久草草在线视视频 | 午夜想想爱 | 3d动漫被吸乳羞羞 | 国产aⅴ一区二区三区 | 色婷婷久久综合中文久久一本` | 韩剧消失的眼角膜免费完整版 | 美女私人影院 |