【Javaweb】Cookie和Session

gonghr發表於2021-07-27

會話技術

什麼是會話

從瀏覽器訪問伺服器開始,到訪問伺服器結束,瀏覽器關閉為止的這段時間內容產生的多次請求和響應,合起來叫做瀏覽器和伺服器之間的一次會話

會話管理作用

共享資料用的,並且是在不同請求間實現資料共享。

會話技術是為了解決客戶端瀏覽器和服務端的通訊問題。使用者在網頁上的操作會產生很多重要的並且需要被儲存的資料。比如購物車資訊,可能我們此時不會清空購物車,那麼購物車的資訊需要被完整的儲存下來。再比如登陸網站只需要登陸一次,再登陸網站的其他網頁時就不需要重複登陸了,這是因為使用者資訊被儲存了。

資料能否用HttpServletRequest或者ServletContext儲存呢?

不能用 HttpServletRequest 的原因:我們的一次會話中,存在多次請求和響應,而瀏覽器客戶端的每一次請求都會產生一個 HttpServletRequest 物件,它只會儲存此次請求的資訊,例如放入購物車與購買付款是不同的請求,很顯然資料沒有得到很好的儲存處理

不能用 ServletContext 的原因:ServletContext物件是被整個web應用所共享的,將資料都存到這裡,無疑會無法區分具體資訊的歸屬

會話管理分類

客戶端會話管理技術

它是把要共享的資料儲存到了客戶端(也就是瀏覽器端)。每次請求時,把會話資訊帶到伺服器,從而實現多次請求的資料共享。

服務端會話管理技術

它本質仍是採用客戶端會話管理技術,只不過儲存到客戶端的是一個特殊的標識,並且把要共享的資料儲存到了服務端的記憶體物件中。每次請求時,把這個標識帶到伺服器端,然後使用這個標識,找到對應的記憶體空間,從而實現資料共享。

Cookie

Cookies是伺服器在本地機器上儲存的小段文字並隨每一個請求傳送至同一伺服器,是在客戶端保持狀態的方案,是客戶端瀏覽器的快取檔案,裡面記錄了客戶瀏覽器訪問網站的一些內容。同時,也是HTTP協議請求和響應訊息頭的一部分

常用API

屬性名稱 屬性作用 是否重要
name cookie的名稱 必要屬性
value cookie的值(不能是中文) 必要屬性
path cookie的路徑 重要
domain cookie的域名 重要
maxAge cookie的生存時間。 重要
version cookie的版本號。 不重要
comment cookie的說明。 不重要

注意細節

Cookie有大小,個數限制。每個網站最多隻能存20個cookie,且大小不能超過4kb。同時,所有網站的cookie總數不超過300個。

當刪除Cookie時,設定maxAge值為0。當不設定maxAge時,使用的是瀏覽器的記憶體,當關閉瀏覽器之後,cookie將丟失。設定了此值,就會儲存成快取檔案(值必須是大於0的,以秒為單位)。

常用方法

返回值 方法 作用
String getName() 返回cookie的name
String getValue() 返回當前cookie的value
void setMaxAge() 設定Cookie最大存活時間

構造方法

Cookie(String name, String value)
  • 通過指定的名稱和值構造一個Cookie
  • Cookie的名稱必須遵循RFC 2109規範。這就意味著,它只能包含ASCII字母數字字元,
  • 不能包含逗號、分號或空格或以$字元開頭。
  • 建立後無法更改cookie的名稱。
  • 該值可以是伺服器選擇傳送的任何內容。
  • 它的價值可能只有伺服器才感興趣。
  • 建立之後,可以使用setValue方法更改cookie的值。

向瀏覽器新增Cookie

public void addCookie(Cookie cookie);
  • 新增Cookie到響應中。此方法可以多次呼叫,用以新增多個Cookie。

從伺服器端獲取Cookie:

 public Cookie[] getCookies();
  • 這是HttpServletRequest中的方法。
  • 它返回一個Cookie的陣列,包含客戶端隨此請求傳送的所有Cookie物件。
  • 如果沒有符合規則的cookie,則此方法返回null。

Cookie的路徑限制

取自第一次訪問的資源路徑字首
只要以這個字首為開頭(包括子級路徑),可以獲取到
反之獲取不到

舉例:

@WebServlet("/servlet/servletDemo02")
public class ServletDemo02 extends HttpServlet{
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //建立Cookie並新增
        Cookie cookie = new Cookie("username","zhangsan");
        cookie.setMaxAge(3600);
        resp.addCookie(cookie);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req,resp);
    }
}
@WebServlet("/servlet/servletDemo03")
public class ServletDemo03 extends HttpServlet{
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //獲取Cookie
        Cookie[] arr = req.getCookies();
        for(Cookie c : arr) {
            if("username".equals(c.getName())) {
                String value = c.getValue();
                resp.getWriter().write(value);
            }
        }
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req,resp);
    }
}
@WebServlet("/servlet/aaa/servletDemo04")
public class ServletDemo04 extends HttpServlet{
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //獲取Cookie
        Cookie[] arr = req.getCookies();
        for(Cookie c : arr) {
            if("username".equals(c.getName())) {
                String value = c.getValue();
                resp.getWriter().write(value);
            }
        }
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req,resp);
    }
}
@WebServlet("/bbb/servletDemo05")
public class ServletDemo05 extends HttpServlet{
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //獲取Cookie
        Cookie[] arr = req.getCookies();
        for(Cookie c : arr) {
            if("username".equals(c.getName())) {
                String value = c.getValue();
                resp.getWriter().write(value);
            }
        }
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req,resp);
    }
}

第一次訪問的路徑是/servlet/servletDemo02
字首是\servlet
/servlet/servletDemo03/servlet/aaa/servletDemo04可以獲取到Cookie的值
/bbb/servletDemo05不能獲得Cookie的值,因為字首不同

/*
    Cookie的使用
 */
@WebServlet("/servletDemo01")
public class ServletDemo01 extends HttpServlet{
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //1.通過響應物件寫出提示資訊
        resp.setContentType("text/html;charset=UTF-8");
        PrintWriter pw = resp.getWriter();
        pw.write("歡迎訪問本網站,您的最後訪問時間為:<br>");

        //2.建立Cookie物件,用於記錄最後訪問時間
        Cookie cookie = new Cookie("time",System.currentTimeMillis()+"");

        //3.設定最大存活時間
        //cookie.setMaxAge(3600);
        cookie.setMaxAge(0);    // 立即清除

        //4.將cookie物件新增到客戶端
        resp.addCookie(cookie);

        //5.獲取cookie
        Cookie[] arr = req.getCookies();
        for(Cookie c : arr) {
            if("time".equals(c.getName())) {
                //6.獲取cookie物件中的value,進行寫出
                String value = c.getValue();
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                pw.write(sdf.format(new Date(Long.parseLong(value))));
            }
        }
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req,resp);
    }
}

Session

Session概述

Cookie 在客戶端瀏覽器儲存Session標識JSESSIONID,而每一個JSESSIONID對應的資料儲存在伺服器,客戶端每次發出請求時會一塊把JSESSIONID傳送給伺服器,伺服器通過這個表實標識尋找對應的資料並把其響應給客戶端。
當使用者在應用程式的 Web頁間跳轉時,儲存在 Session 物件中的變數不會丟失而是在整個使用者會話中一直存在下去。

HttpSession 物件

它是Servlet規範中提供的一個介面。該介面的實現由Servlet規範的實現提供商提供。我們使用的是Tomcat伺服器,它對Servlet規範進行了實現,所以HttpSession介面的實現由Tomcat提供。該物件用於提供一種通過多個頁面請求或訪問網站來標識使用者並儲存有關該使用者的資訊的方法。簡單說它就是一個服務端會話物件,用於儲存使用者的會話資料。

同時,它也是Servlet規範中四大域物件之一的會話域物件。並且它也是用於實現資料共享的。

域物件 作用範圍 使用場景
ServletContext 整個應用範圍 當前專案中需要資料共享時,可以使用此域物件。
ServletRequest 當前請求範圍 在請求或者當前請求轉發時需要資料共享可以使用此域物件。
HttpSession 會話返回 在當前會話範圍中實現資料共享。它可以在多次請求中實現資料共享。

HttpSession 常用方法

獲取HttpSession物件的方法:

public HttpSession getSession();
public HttpSession getSeesion(boolean create);

image

image

Session 使用案例

/*
    Session的基本使用
 */
@WebServlet("/servletDemo01")
public class ServletDemo01 extends HttpServlet{
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //1.獲取請求的使用者名稱
        String username = req.getParameter("username");

        //2.獲取HttpSession的物件
        HttpSession session = req.getSession();
        System.out.println(session);
        System.out.println(session.getId());

        //3.將使用者名稱資訊新增到共享資料中
        session.setAttribute("username",username);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req,resp);
    }
}
/*
    Session的基本使用
 */
@WebServlet("/servletDemo02")
public class ServletDemo02 extends HttpServlet{
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //1.獲取HttpSession物件
        HttpSession session = req.getSession();
        System.out.println(session);
        System.out.println(session.getId());

        //2.獲取共享資料
        Object username = session.getAttribute("username");

        //3.將資料響應給瀏覽器
        resp.getWriter().write(username+"");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req,resp);
    }
}

Cookie禁用時的處理方案

方案一(推薦)
直接提示Cookie被禁用了

/*
    Cookie的禁用
 */
@WebServlet("/servletDemo03")
public class ServletDemo03 extends HttpServlet{
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //1.獲取HttpSession物件
        HttpSession session = req.getSession(false); //若JSESSIONID不存在,則不建立新HttpSession物件
        System.out.println(session);
        if(session == null) {
            resp.setContentType("text/html;charset=UTF-8");
            resp.getWriter().write("為了不影響正常的使用,請不要禁用瀏覽器的Cookie~");
        }
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req,resp);
    }
}

方案二(重寫URL)

客戶端瀏覽器如果關閉Cookie許可權,就無法把JSESSIONID傳給伺服器,如果想實現頁面共享,就需要把JSESSIONID通過URL傳遞給伺服器。也就是把JSESSIONID直接附加在URL路徑的後面。

String encodeURL(String url)

Encodes the specified URL by including the session ID, or, if encoding is not needed, returns the URL unchanged. The implementation of this method includes the logic to determine whether the session ID needs to be encoded in the URL. For example, if the browser supports cookies, or session tracking is turned off, URL encoding is unnecessary.

@WebServlet("/servletDemo01")
public class ServletDemo01 extends HttpServlet{
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //1.獲取請求的使用者名稱
        String username = req.getParameter("username");

        //2.獲取HttpSession的物件
        HttpSession session = req.getSession();
        System.out.println(session);
        System.out.println(session.getId());

        //3.將使用者名稱資訊新增到共享資料中	
        session.setAttribute("username",username);

        //實現url重寫  相當於在位址列後面拼接了一個jsessionid
        resp.getWriter().write("<a href='"+resp.encodeURL("http://localhost:8080/session/servletDemo03")+"'>go servletDemo03</a>");

    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req,resp);
    }
}

點選連線以後,發現URL顯示http://localhost:8080/ServletDemo13;jsessionid=AEAFBD306F72BF3379A660596277E4C9
encodeURL方法使地址自動附加了JSESSIONID

HttpSession的鈍化和活化

什麼是持久態

​ 把長時間不用,但還不到過期時間的HttpSession進行序列化,寫到磁碟上。

​ 我們把HttpSession持久態也叫做鈍化。(與鈍化相反的,我們叫活化。)

什麼時候使用持久化

​ 第一種情況:當訪問量很大時,伺服器會根據getLastAccessTime來進行排序,對長時間不用,但是還沒到過期時間的HttpSession進行持久化。

​ 第二種情況:當伺服器進行重啟的時候,為了保持客戶HttpSession中的資料,也要對HttpSession進行持久化

注意

​ HttpSession的持久化由伺服器來負責管理,我們不用關心。

​ 只有實現了序列化介面的類才能被序列化,否則不行。

相關文章