session及cookies理解(結(jié)合購物車和用戶登錄案例)

總結(jié):

以下幾個案例難點在于:
a、什么時候需要創(chuàng)建session,什么時候只是讀?。ㄉ婕癵etsession方法的參數(shù)),還需要了解一下getsession方法是如何實現(xiàn)的,為什么好像沒有訪問cookies就得到了session。
b、什么時候要向瀏覽器發(fā)送cookies,使用哪個Servlet向瀏覽器發(fā)送cookies。

(注:由于案例比較簡單,更多是起示范作用,所以很少引入html頁面和表單,很多時候是直接通過在瀏覽器地址欄輸入URL來直接訪問Servlet)。


案例1:(cookie案例)——顯示用戶上次訪問時間

??該案例要實現(xiàn)的是當(dāng)用戶訪問某些web應(yīng)用時,顯示出上次的訪問時間。
??該案例只用到一個LastAccessServlet,并且不需要多余的頁面(例如表單),結(jié)果也是直接通過字符流打印出來顯示在瀏覽器上。通過localhost/chapter06/LastAccessServlet來進(jìn)行訪問。(其中chapter06該案例對應(yīng)的應(yīng)用的名稱,而/LastAccessServlet會跳轉(zhuǎn)到LastAccessServlet這個類)
??接下來,request會獲取cookies中name為lastAccessTime的cookie,首先request請求先獲取cookies。這時候會有cookies為空和非空兩種可能性。只有cookies非空我們才對它進(jìn)行遍歷,如果能找到name為LastAccessTime的cookie,那么我們就獲取它的值,如果遍歷完成還沒找到,或者本身cookies就為空,這兩種情況我們都?xì)w納為lastAccessTime== null,在該條件下在瀏覽器返回“首次訪問本站”的信息。否則就將LastAccessTime的值顯示出來。

//  LastAccessServlet類doGet方法中的部分代碼,request代表HttpServletRequest請求。
String lastAccessTime = null;
Cookie[] cookies = request.getCookies();
for (int i=0;cookies != null && i<cookies.length;i++){
    if("lastAccess".equals(cookies[i].getName())){
        lastAccessTime = cookies[i].getValue();
        break;
    }
}
if(lastAccessTime == null){...//返回首次訪問本站信息
}else{
    ...//返回上次訪問時間
}

??這一切結(jié)束后,再將lastAccess賦值為當(dāng)前時間,然后通過HttpServletResponse對象將其添加到cookie中。

String currenttime = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date());
response.addCookie(cookie);

案例2(session案例)——實現(xiàn)購物車

??這里的購物車只是一個比較簡單的模型,沒有涉及數(shù)據(jù)庫的內(nèi)容。用一個BookDB.java來模擬保存所有圖書(定義為Book類)的數(shù)據(jù)庫,說的直白一些,就是在BookDB里面定義了一個Map集合(之所以在這里使用集合而不使用數(shù)組,一是因為可以增刪,長度不固定,二是這里保存的是鍵值對。鍵(id)在后續(xù)中用于實現(xiàn)查詢功能。而在 purchaseServlet中,由于只需要遍歷而不需要查詢,所以使用的是ArrayList)鍵(id)的查詢功能包括兩個:一是根據(jù)指定的id來獲得一個圖書對象。這個功能本質(zhì)上是通過Map集合的get(Object key)方法來實現(xiàn),返回Object類型。二是獲取所有的圖書對象集合,這個功能通過Map集合的values()方法實現(xiàn),返回Collection<V> 類型。這個集合里面的一個元素由序號和Book組成。

public class BookDB {
private static Map<String, Book> books = new LinkedHashMap<String, Book>();
static {
    books.put("1", new Book("1", "javaweb開發(fā)"));
    ......
    books.put("5", new Book("5", "spring開發(fā)"));
    }
// 獲得所有的圖書
    public static Collection<Book> getAll() {
        return books.values();
    }
// 根據(jù)指定的id獲得圖書
    public static Book getBook(String id) {
        return books.get(id);
    }
}

??不同的對象就好像象棋中不同的棋子(例如車馬象),有不同的“走法”(方法)。我們當(dāng)然都希望他們能夠橫豎甚至對角線都能走,但是做不到。 每個對象都有它們自己特定的方法這里除了request和response對象以外,還需要引入ListBookServlet、PurchaseServlet和CartServlet。
需要的Servlet的多少是根據(jù)我們的業(yè)務(wù)需求來決定的。

購物車的實現(xiàn)流程圖

??第一個需求就是訪問某個Servlet(這里是ListBookServlet)列出我們的所有“可購買書單”。上面說過,列出所有書單的功能Map的values()方法已經(jīng)能夠?qū)崿F(xiàn)(甚至直接BookDB類就可以觸發(fā)方法,而不需要實例化),而response對象能將其遍歷的結(jié)果通過輸出流顯示到瀏覽器上。而ListBookServlet除了實現(xiàn)這兩個基本的功能,還在輸出結(jié)果時,在遍歷的每個條目的后面添加一個購買鏈接(顯示為“點擊購買”字樣)。一旦我們點擊了鏈接,就會跳轉(zhuǎn)到PurchaseServlet,同時還會帶上一個id參數(shù)(這部分代碼如下所示)。到這里,第一個Servlet(ListsBookServlet)的功能就結(jié)束了。

    for (Book book : books) {
        String url = "/chapter06/PurchaseServlet?id=" + book.getId();
        out.write(book.getName() + "<a href='" + url 
                + "'>點擊購買</a><br>");
    }

??接著我們的PurchaseServlet出場了,他面對的是一個新的請求,這個請求是從ListBookServlet跳轉(zhuǎn)而來,后面帶有一個id參數(shù)。如果沒有這個id,表示可能直接由瀏覽器輸入localhost:/chapter06/PurchaseServlet發(fā)起的請求,這時候就需要重定向回到 ListBookServlet,這么做是確保我們的PurchaseServlet面對的請求帶有id這個參數(shù)。

??當(dāng)存在id參數(shù)時,表明已經(jīng)有用戶點擊購買。這時候通過getSession()方法(手動)獲取或者創(chuàng)建一個HttpSession對象,這個session用于存放我們的信息。
request.getSession()方法等同于request.getSession(True),它意味著能夠在Httpsession對象不存在時創(chuàng)建新的session對象。
注:這時候如果沒有session服務(wù)器是需要創(chuàng)建session的。因為我們需要session來存放這個購買請求。所以我們使用的是getSession(True)而不是getSession(False)方法。

??至于為什么來自客戶端的request調(diào)用getSession方法就可以訪問到服務(wù)端的session信息。我的理解是request(包裝后的 HttpServlet對象)先查詢自己的cookies,根據(jù)cookies中的session關(guān)聯(lián)id獲取到存放于服務(wù)器中的session對象。
??session的關(guān)聯(lián)號存放于cookies中,而cookies存放于瀏覽器端,在發(fā)起特定請求后可以理解為cookies信息被封裝到 request對象中,所以通過request.getSession()方法可以獲取cookies中的session關(guān)聯(lián)號,再據(jù)此找到對應(yīng)的session
??當(dāng)我們在瀏覽器地址欄中直接訪問cartServlet,想讀取已購買數(shù)目信息,我們的服務(wù)器就需要讀取session,所以在cartServlet之前,也即是PurchaseServlet這里就應(yīng)該發(fā)送cookies到客戶端了。
??而在案例3中,我們會直接訪問login.html,并且這個請求同樣需要讀取對應(yīng)的 session,所以我們應(yīng)該在login.html之前,也就是indexServlet這里,將我們的cookies發(fā)回給瀏覽器。只有這樣,當(dāng)我們直接訪問時,才能夠通過getSession()方法來找到對應(yīng)的session對象。

??session有兩個比較常用的方法,一個是getAttribute(),用于返回session中指定名稱的屬性對象。另外一個是getID,用于返回與當(dāng)前HttpSession對象關(guān)聯(lián)的會話標(biāo)識號。
??在purchaseServlet中只需要一個ArrayList(而不用 Map)來存儲cart(購物車)的值就可以了。

    List<Book> cart = (List) session.getAttribute("cart");
    if (cart == null) {
        // 首次購買,為用戶創(chuàng)建一個購物車(List集合模擬購物車)
        cart = new ArrayList<Book>();
        // 將購物城存入Session對象
        session.setAttribute("cart", cart);
    }
    // 將商品放入購物車
    cart.add(book);

??然后創(chuàng)建cookie用于存放session的標(biāo)識號。并且這時候其實purchaseServlet沒有向用戶顯示任何的信息,只是進(jìn)行了一些后臺的操作,然后繼續(xù)跳轉(zhuǎn)到CartServlet。
CartServlet先獲得用戶的session.

HttpSession session = req.getSession(false);

??這里使用 false表明如果當(dāng)前沒有session存在,那么session = null,而不會繼續(xù)新建一個session對象。 最終是想要讀取session中名為“cart”的ArrayList集合。并通過字符流將book對象的名字打印出來。
??那么為什么不將CartServlet和purchaseServlet結(jié)合在一起呢。
這里需要搞清楚這個案例的意義,在這個案例中,用戶直接訪問/ListBookServlet進(jìn)行購買(當(dāng)我們點擊購買鏈接以后就在瀏覽器上顯示我們已經(jīng)購買了這本書),而直接訪問cartServlet用于查看已經(jīng)購買的書。想要重新購買新的書時,需要再次訪問/ListBookServlet來進(jìn)行購買。如果將PurchaseServlet和CartServlet結(jié)合在一起。就沒有辦法制作出不帶session的訪問請求了。而現(xiàn)在,在請求跳轉(zhuǎn)到CartServlet前,已經(jīng)將帶有session標(biāo)識號的cookie發(fā)送到客戶端了,這時候無論是從哪里再跳轉(zhuǎn)到Servlet,都可以訪問到session了。在應(yīng)用中跳轉(zhuǎn)過去的可以,直接從瀏覽器地址欄訪問的也可以。但是手動刪除瀏覽器的cookies,那么再直接訪問就訪問不到session了。


案例3(session案例)——實現(xiàn)用戶登錄

回顧文章開頭提出的兩個難點:
a、什么時候創(chuàng)建session,什么時候只是讀?。╣etsession方法)
b、什么時候要向瀏覽器發(fā)送cookies。

??可以這樣理解,如果訪問時需要這些session信息,例如通過瀏覽器訪問Login.html時要想成功登錄,就需要讀取session信息,那么就需要在這之前提前發(fā)送cookies。所以在這個案例中,我們是在indexServlet中發(fā)送cookies信息。而在前面的案例中,我們希望直接訪問cartServlet來讀取我們當(dāng)前購買了什么書,那么在這個cartServlet之前就必須要將cookies傳送完畢。

??其實從流程圖就可以看出來,loginservlet和logoutservlet都是會連接到我們的indexservlet的。loginservlet的作用是將表單的內(nèi)容創(chuàng)建成為一個user對象保存到session中。而logoutservlet則是將我們的user對象從session中進(jìn)行刪除。然后indexservlet先從服務(wù)器中讀取我們的session,如果有就登錄成功,沒有就跳轉(zhuǎn)到登錄頁面。并且只有當(dāng)存在對象的時候,才會將我們的session使用 getid的方法保存到cookie中。

能不能在loginServlet中發(fā)送cookies?

??loginservlet還能通過“注銷”按鈕跳轉(zhuǎn)到logoutservlet,當(dāng)我們登錄跳轉(zhuǎn)到loginservlet,如果用戶信息驗證通過就發(fā)送cookies的話,這時候再注銷,session里面的對象就沒有了,但是cookies卻已經(jīng)發(fā)出去了。那么我們再次訪問時,cookies里面的內(nèi)容就對不上了。這樣的邏輯容易混亂。

能不能實現(xiàn)用戶自動登錄功能?

??這里只是實現(xiàn)了用戶登錄功能,并沒有實現(xiàn)用戶自動登錄功能。原因在于該案例中用戶想要登錄時都必須經(jīng)過Login.html,然后再經(jīng)過LoginServlet驗證。也就是說,雖然我們的indexServlet已經(jīng)將cookies發(fā)送到瀏覽器端。但是用戶想登錄還是要老老實實通過Login.html,如果能夠?qū)⑷魏我粋€請求都繞過Login.html,直接跳轉(zhuǎn)到LoginServlet進(jìn)行驗證,如果通過的話不就能實現(xiàn)自動登錄了嗎?Filter就可以用來實現(xiàn)這樣的功能。

??在Filter實現(xiàn)用戶自動登錄的案例中,可以看到,AutoLoginFilter中同樣有LoginServlet中一樣的用于驗證用戶信息的代碼(其實就相當(dāng)于這里提出的跳轉(zhuǎn)到LoginServlet的思路)。另外,F(xiàn)ilter案例中沒有IndexServlet,所以發(fā)送cookie的代碼在loginServlet和LogoutServlet中都要有。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 會話(Session)跟蹤是Web程序中常用的技術(shù),用來跟蹤用戶的整個會話。常用的會話跟蹤技術(shù)是Cookie與Se...
    chinariver閱讀 5,778評論 1 49
  • 目錄Cookie機(jī)制什么是CookieCookie的不可跨域名性Unicode編碼:保存中文BASE64編碼:保存...
    Tomatoro閱讀 17,041評論 7 186
  • 從三月份找實習(xí)到現(xiàn)在,面了一些公司,掛了不少,但最終還是拿到小米、百度、阿里、京東、新浪、CVTE、樂視家的研發(fā)崗...
    時芥藍(lán)閱讀 42,759評論 11 349
  • 夢境: 跟著一個男的假裝去見家長,然后他媽媽又是夾菜又是塞錢的,好到不好意思…… 單詞15 看書:《所羅門...
    是魔王大人閱讀 207評論 5 0
  • 6月21日,夏至節(jié)氣,一大早四點就起床去鏡湖拍日出,結(jié)果剛剛霞光微露就蹤影全無, 幸好濕地深處荷花綻放,就隨手拍幾...
    我愛蘋果絲閱讀 478評論 2 1

友情鏈接更多精彩內(nèi)容