基於Cookie與Session的Servlet API會話管理操作

⭐️前面的話⭐️

本篇文章將介紹Servlet中的會話管理操作,前面在介紹HTTP的時候,我們已經說瞭Cookie與Session的基本原理,Cookie相當於就診卡,就診卡一刷得到的信息就相當於Session,Servlet對Cookie與Session有著很好的支持,可以實現會話的管理操作。

🍒1.Cookie與Session

🍇1.1回顧與理解Cookie與Session

通過學習HTTP,我們知道HTTP協議是“無狀態”協議,這裡的“無狀態”指的是默認情況下 HTTP 協議的客戶端和服務器之間的當次通信, 和下次通信之間沒有直接的聯系,但是在實際開發中是需要建立起來聯系的,比如一個網站的登錄,在生活中,我們登錄一次網站後,再退出網站後,再次登錄網站,我們會發現並不需要再次輸入賬號密碼登錄,網站會自動地幫助我們登錄。

為瞭實現類似這種網站的自動登錄,在用戶第一次輸入賬號密碼登錄的時候,服務器會創建一個Session會話來保存當前用戶的數據和信息,和生成一個Cookie,Cookie裡面含有該Session裡面的一些關鍵身份信息,服務器會將這個Cookie作為響應給客戶端,客戶端或者說是瀏覽器就會將這個Cookie儲存起來,當下一次登錄時,瀏覽器發送請求的時候就會帶上這個Cookie,服務器收到請求後會去獲取請求中的Cookie列表,並去查詢服務器中是否存在對應的Session,如果存在就會自動登錄,不用用戶輸入賬號與密碼,否則需要用戶輸入賬號密碼進行登錄。

打個比方來說,去醫院看病需要先掛號,如果你沒有當前醫院的就診卡,就會為你新辦理一個就診卡,這個就診卡裡面含有你的一些關鍵身份信息,並且會在醫院的服務器上新建一個檔案,得到這個就診卡,你就可以在該醫院的各個科室進行刷卡,如果你之前在這個醫院有就診記錄,你一刷卡就可以查詢到你所有在當前醫院的就診信息。 上面的這個就診卡就相當於這裡的Cookie,上面有你最基本的身份信息,在醫院服務器上所儲存有關你的詳細信息,就相當於一個會話,也就是一個Session,當然服務器上不止你一個人的Session,它包含很多用戶的信息。

這個Cookie與Session機制最主要的作用就是用來識別用戶的身份信息。

🍇1.2Servlet會話管理操作

HttpServletRequest類中,可以使用getSession來獲取或創建會話與getCookies可以獲取請求中的Cookie列表。

方法 描述
HttpSession getSession() 在服務器中獲取會話. 參數如果為 true, 則當不存在會話時新建會話; 參數如果為 false, 則當不存在會話時返回 null
Cookie[] getCookies() 返回一個數組, 包含客戶端發送該請求的所有的 Cookie 對象. 會自動把Cookie 中的格式解析成鍵值對.

對於getCookies方法,由於Cookie能夠任意自定義鍵值對,如果想要獲取一般的鍵值對,可以使用該方法獲取,但是如果想要獲取特殊的鍵值對,如SessionId,可以通過getSession直接獲取,沒必要通過getCookies方法的途徑來獲取,因為getSession方法它自動幫助我們獲取瞭SessionId

調用getSession方法所做的事情:

創建會話:

  • 第一步,獲取cookie裡面的sessionId字段,相當於會話標識。
  • 第二步,判斷是否在服務器上存在。
  • 第三步,如果不存在,則創建一個新的HttpSession對象,並生成一個新的sessionId
  • 第四步,接下來就會以新生成的sessionId作為key,生成的HttpSession對象作為value,以鍵值對形式儲存到類似與哈希表的結構中。
  • 第五步,返回響應,將sessionId通過set-Cookie字段返回給瀏覽器,這樣瀏覽器就得到瞭sessionId,瀏覽器就可以將SessionId保存到Cookie中瞭。

獲取會話:

  • 第一步,獲取cookie裡面的sessionId字段,相當於會話標識。
  • 第二步,判斷是否在服務器上存在。
  • 第三步,直接查詢HttpServlet對象,找到直接作為響應返回給客戶端。

關於HttpSession 這個對象本質上也是一個鍵值對的形式,並且允許程序員在對象中儲存任意的鍵值對數據,但是key必須是String, value隨意。

HttpSession裡面的每一個鍵值對稱為屬性(Attribute),該類中提供瞭兩個方法可以用來獲取該對象中的屬性和儲存屬性(鍵值對)。

方法 描述
Object getAttribute(String name) 該方法返回在該 session 會話中具有指定名稱的對象,如果沒有指定名稱的對象,則返回 null.
void setAttribute(String name, Object value) 該方法使用指定的名稱綁定一個對象到該 session 會話
boolean isNew() 判定當前是否是新創建出的會話

Cookie類常用方法 Cookie對象包含瞭兩個屬性,一個是name,另一個是value,而在請求中是以鍵值對的形式儲存的,服務器收到請求後會將鍵值對形式的cookie轉換為Cookie對象。

方法 描述
String getName() 該方法返回 cookie 的名稱。名稱在創建後不能改變。(這個值是 Set-Cooke 字段設置給瀏覽器的)
String getValue() 該方法獲取與 cookie 關聯的值
void setValue(String newValue) 該方法設置與 cookie 關聯的值。

在響應中添加Cookie 我們可以通過HttpServletResponse類中的addCookie方法來在響應中添加Cookie,它會作為HTTP響應中set-Cookie字段來進行表示。

方法 描述
void addCookie(Cookie cookie) 把指定的 cookie 添加到響應中.

🍒2.常見案例實現

首先我們先得創建Maven文件,引入依賴,創建必要的路徑,這些過程在之前介紹Servlet使用已經詳細介紹瞭,這裡就不再進行贅述瞭,我們的重點是前端與後端交互過程的實現。

🍇2.1登錄邏輯的實現

我們經常上網查詢一些網站什麼的,很多網站都會讓你先登錄,才能使用其中的一些功能,登錄完成之後,一般都會跳到一個主頁網站,下面我們就來簡單地實現一下這一套邏輯。

第一步,約定前後端接口。 我們需要實現兩套交互邏輯,一是登錄跳轉,二是獲取主頁。 登錄跳轉約定: 約定使用POST請求,響應采用302重定向。

獲取主頁約定: 采用GET請求,響應返回一個頁面。

第二步,編寫前端交互頁面 我們的重點是來學習登錄的邏輯,因此登錄的界面不需要很好看很復雜,隻要能夠有兩個輸入框和一個提交按鈕讓我們輸入賬號密碼就行。目標頁面如下:

前面我們約定瞭登錄的跳轉采用post請求,由於場景很簡單,我們直接使用form表單構造post請求就可以瞭。

<!DOCTYPE html>
<html lang="ch">
<head>
    <meta charset="UTF-8">
    <title>login</title>
</head>
<body>
    <form action="login" method="post">
        <input type="text" name="username">
        <input type="password" name="password">
        <input type="submit" value="登錄">
    </form>
</body>
</html>

其中input標簽的name屬性就對應鍵值對的key,輸入的內容就對應鍵值對的value

第三步,編寫後端處理代碼

對於登錄跳轉頁面post請求處理思路如下:

  • 從請求中獲取賬號與密碼。
  • 驗證賬號與密碼。
  • 如果驗證通過,創建會話,並將username數據加入到會話中,當然還可以加入其它的屬性,比如主頁被訪問的次數,創建好會話後,重定向到主頁index
  • 如果驗證不通過,告知登錄失敗即可。
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("utf8");
        resp.setCharacterEncoding("utf8");
        //獲取用戶賬號
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        //驗證賬戶
        //驗證按照正常流程應該從數據庫讀數據,但是為瞭便於演示登錄的邏輯,我們直接將賬號密碼寫死
        //假設正確的賬號與密碼是 zhangsan 123
        if ("zhangsan".equals(username) && "123".equals(password)) {
            //登錄成功
            //創建會話,為後續需登錄的頁面做準備
            HttpSession httpSession = req.getSession(true);
            httpSession.setAttribute("username", username);
            //初始情況下設置登錄次數
            httpSession.setAttribute("count", 0);
            //跳轉到目標頁面index
            resp.sendRedirect("index");
        } else {
            //登錄失敗
            resp.getWriter().write("登錄失敗!");
        }
    }
}

獲取主頁的get請求處理思路:

  • 獲取會話。
  • 取出會話信息,將主頁返回次數加1並寫回到會話信息中。
  • 返回一個簡單的頁面。
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
@WebServlet("/index")
public class IndexServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //返回一個主頁
        //獲取會話,參數必須是false
        HttpSession httpSession = req.getSession(false);
        //取出會話信息
        String username = (String) httpSession.getAttribute("username");
        Integer cnt = (Integer) httpSession.getAttribute("count");
        //訪問次數加1
        cnt++;
        //寫回到會話中
        httpSession.setAttribute("count", cnt);
        //構造頁面。我們簡單構造一下就好
        resp.setContentType("text/html; charset=utf8");
        resp.getWriter().write("<h4>歡迎您!" + username + "</h4> <h4>這個主頁已經被訪問瞭" + cnt + "次</h4>");
    }
}

抓包結果:

第一次交互,瀏覽器從服務器上拿到登錄頁面。 第二次交互,瀏覽器給服務器一個登錄請求,服務器返回響應,重定向頁面。 第三次交互,瀏覽器收到302重定向響應後,再次向服務器發起請求,訪問主頁。

效果演示:

🍇2.2上傳文件

上傳文件時,前端需要使用到form表單,表單中需要使用一種特殊的類型,叫做form-data類型。

提交文件的時候,瀏覽器會把文件以form-data的格式構造到Http請求中,服務器可以通過getPart方法來取的Part對象(文件),再通過Part對象就能夠獲取到文件信息瞭。

方法 描述
Part getPart(String name) 獲取請求中給定 name 的文件
Collection getParts() 獲取所有的文件

Part類常用方法:

方法 描述
String getSubmittedFileName() 獲取提交的文件名
String getContentType() 獲取提交的文件類型
long getSize() 獲取文件的大小
void write(String path) 把提交的文件數據寫入磁盤文件

上傳文件的請求采用post請求,使用form表單構造post請求,我們來實現將一個文件上傳到本機的一個目錄下。

前端交互頁面:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>file</title>
</head>
<body>
    <form action="upload" method="post" enctype="multipart/form-data">
        <input type="file" name="myfile">
        <input type="submit" value="提交">
    </form>
</body>
</html>

後端處理代碼:

基本實現思路:

  • 從請求中獲取Part對象。
  • 調用Part對象的一些方法,獲取文件信息,在磁盤寫文件等。
  • 返回響應。
  • 加上@MultipartConfig註解,不然會出錯。
import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
import java.io.IOException;
@MultipartConfig
@WebServlet("/upload")
public class UploadServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //獲取Part對象
        Part part = req.getPart("myfile");
        //輸出文件信息
        //文件名
        System.out.println("文件名:" + part.getSubmittedFileName());
        //文件類型
        System.out.println("文件類型:" + part.getContentType());
        //文件大小
        System.out.println("文件大小:" + part.getSize());
        //將文件寫入磁盤
        part.write("D:\\gtee\\Java代碼\\github\\Login\\src\\main\\webapp\\上傳.png");
        //返回響應
        resp.setContentType("text/html; charset=utf8");
        resp.getWriter().write("上傳成功!");
    }
}

效果演示:

看看我們的目標目錄上有沒有上傳的圖片。

發現我們上傳的文件已經拷貝到我們所寫的磁盤位置上瞭,我們上傳的文件會儲存在響應的body部分,在這個body中,會使用一段隨機的字符串作為標志,表示文件的開始與結束。

以上就是基於Cookie與Session的Servlet API會話管理操作的詳細內容,更多關於Servlet API會話管理的資料請關註WalkonNet其它相關文章!

推薦閱讀: