01-4 會話技術
首先思考一個問題:
在無登陸情況下訪問購物網站,仍然可以新增商品到購物車,那麼這是什麼原因呢?
猜想:
1.資料存放在資料庫
錯誤,無法儲存,沒有主鍵(使用者名稱).也不可能ip地址做主鍵
2.封裝在request和response域物件中
錯誤,域物件的作用範圍很有限,僅在當前訪問內有效,即使採用轉發把request,response傳到別的servlet,那麼加入購物車(訪問購物車servlet)後會立即轉到付款servlet
3.封裝到servletcontext
servletcontext的作用域是整個專案,一個文字物件只有一個servletcontext,後面使用者的k-v會把前面使用者的k-v覆蓋,所以不行
實際上是採用的會話技術,即cookie和session
【會話技術】
從開啟一個瀏覽器訪問某個站點,到關閉這個瀏覽器的整個過程,成為一次會話。
會話技術就是記錄這次會話中客戶端的狀態與資料的。
會話技術分為Cookie和Session:
Cookie:資料儲存在客戶端本地,減少伺服器端的儲存的壓力,安全性不好,客戶端可以清除cookie
Session:將資料儲存到伺服器端,安全性相對好,增加伺服器的壓力
【Cookie】
Cookie技術是將使用者的資料儲存到客戶端的技術
cookie機制
那麼本文開頭的購物網站是如何儲存購物車內容的?
1. 伺服器端向客戶端傳送一個Cookie
1)建立Cookie:
Cookie cookie = new Cookie(String cookieName,String cookieValue);
示例:
Cookie cookie = new Cookie("username","zhangsan");
那麼該cookie會以響應頭的形式傳送給客戶端:
注意:Cookie中不能儲存中文,也不是域物件
2)設定Cookie在客戶端的持久化時間:
cookie.setMaxAge(int seconds); ---時間秒
注意:如果不設定持久化時間,cookie會儲存在瀏覽器的記憶體中,
瀏覽器關閉cookie資訊銷燬(會話級別的cookie),
如果設定持久化時間,cookie資訊會被持久化到瀏覽器的磁碟檔案裡
示例:
cookie.setMaxAge(10*60);
設定cookie資訊在瀏覽器的磁碟檔案中儲存的時間是10分鐘,過期瀏覽器自動刪除該cookie資訊
3)設定Cookie的攜帶路徑:
cookie.setPath(String path);
注意:如果不設定攜帶路徑,那麼該cookie資訊會在訪問產生該cookie的web資源所在的路徑都攜帶cookie資訊
示例:
cookie.setPath("/WEB16");
代表訪問WEB16應用中的任何資源都攜帶cookie
cookie.setPath("/WEB16/cookieServlet");
代表訪問WEB16中的cookieServlet時才攜帶cookie資訊
4)向客戶端傳送cookie:
response.addCookie(Cookie cookie);
package com.wowowo.cookie;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/cookie")
public class CookieServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// cookie分為記憶體形式和檔案形式(預設的是記憶體形式,瀏覽器關閉了cookie就刪除)
//新建一個cookie,不允許有中文
Cookie cookie = new Cookie("gundam", "freedom");
// 設定銷燬時間,以秒為單位,設定了時間的cookie是檔案形式,不會隨瀏覽器關閉而刪除
cookie.setMaxAge(5 * 60);
//訪問某個資源會攜帶cookie
//cookie.setPath("/my1022/cookie");
//訪問my1022專案下的資源會攜帶cookie
// cookie.setPath("/my1022/");
//對主機下所有資源會攜帶cookie
cookie.setPath("/");
// 在response物件裡新增cookie
response.addCookie(cookie);
response.getWriter().append("Served at: ").append(request.getContextPath());
}
}
5)刪除客戶端的cookie:
如果想刪除客戶端已經儲存的cookie資訊,那麼就使用同名同路徑的持久化時間為0的cookie進行覆蓋即可
//新增一個同名空值,同路徑,持久化時間為0秒的cookie
Cookie cookie=new Cookie("gundam","");
cookie.setMaxAge(0);
cookie.setPath("/");
response.addCookie(cookie);
2.伺服器端怎麼接受客戶端攜帶的Cookie
cookie資訊是以請求頭的方式傳送到伺服器端的:
瀏覽器每次訪問都會把url對應的cookie傳給伺服器
//1)通過request獲得所有的Cookie:
Cookie[] cookies = request.getCookies();
//2)遍歷Cookie陣列,通過Cookie的名稱獲得我們想要的Cookie
for(Cookie cookie : cookies){
if(cookie.getName().equal(cookieName)){
String cookieValue = cookie.getValue();
}
}
注意:有些for each迴圈在不知道陣列是否為空的情況下需要判斷:
1.遍歷資料夾
2.遍歷cookies陣列
例題:
/1. 寫一個Servlet, 訪問它可以顯示出使用者上一次的訪問時間
訪問時間以cookie形式儲存一個月. 如果是第一次訪問, 就顯示你是第一次訪問
package com.wowowo.cookie;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import jdk.nashorn.internal.runtime.regexp.joni.Config;
@WebServlet("/show")
public class ShowCookieTime extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 設定編碼格式為utf-8
response.setContentType("text/html;charset=UTF-8");
// 獲取cookies陣列
Cookie[] cookies = request.getCookies();
// 設定一個標誌位,用來判斷是否是第一次登入
boolean isFirst = true;
// cookie不為空的情況下進行遍歷,防止空指標
if (cookies != null) {
// 遍歷cookies,查詢是否是第一次訪問(查詢cookie的name為time是否存在)
for (Cookie cookie1 : cookies) {
// 存在name為time的鍵值對
if ("time".equals(cookie1.getName())) {
isFirst = false;
// 列印上一次訪問時間
response.getWriter().write("上一次訪問時間" + cookie1.getValue());
// break可加可不加,加的話提高效率
break;
}
}
}
// isFirst為false的情況為: cookie不為空而且裡面有一個name為time的鍵值對,也就是說不是第一次訪問
// 反過來就是cookie為空或者cookie內沒有time鍵值對,表示第一次訪問
if (isFirst) {
response.getWriter().write("第一次訪問");
}
// 包裝一個cookie帶格式化當前時間的鍵值對新增到響應物件傳送過去
Cookie cookie = new Cookie("time", new SimpleDateFormat("YYYY-MM-dd HH:mm:ss").format(new Date()));
cookie.setPath("ShowCookieTime/show");
cookie.setMaxAge(30 * 24 * 60 * 60);
response.addCookie(cookie);
}
}
【Session】
session的機制
此處舉個存包系統的例子,方便理解session的機制:
牌子儲存在寄包人--sessionid(儲存在瀏覽器的cookie裡面)
包儲存在櫃子--session的內容(儲存在伺服器)
1.第一次寄包(第一次訪問伺服器)
管理員會發放一塊牌子,牌子的號碼對應包在櫃子所放的位置
伺服器如果發現cookie中不存在sessionid,那麼就會新建一個session並把sessionid返回給使用者
2.包寄存在櫃子裡,想來拿包(不是第一次訪問伺服器)
寄包人把牌子給管理員,管理員根據牌子的號碼找到包
瀏覽器把sessionid放在請求頭的cookie中傳送到伺服器,伺服器根據sessionid獲取相應的session內容做進一步處理
Session技術是將資料儲存在伺服器端的技術,會為每個客戶端都建立一塊記憶體空間儲存客戶的資料,
但客戶端需要每次都攜帶一個標識ID去伺服器中尋找屬於自己的記憶體空間。
所以說Session的實現是基於Cookie,
Session需要藉助於Cookie儲存客戶的唯一性標識JSESSIONID
1.獲得Session物件
HttpSession session = request.getSession();
此方法會獲得專屬於當前會話的Session物件,
- 如果伺服器端沒有該會話的Session物件會建立一個新的Session返回,
- 如果已經有了屬於該會話的Session直接將已有的Session返回
(實質就是根據JSESSIONID判斷該客戶端是否在伺服器上已經存在session了)
2.怎樣向session中存取資料(session也是一個域物件)
Session也是儲存資料的區域物件,所以session物件也具有如下三個方法:
session.setAttribute(String name,Object obj);
session.getAttribute(String name);
session.removeAttribute(String name);
3.Session物件的生命週期(面試題/筆試題)
建立:第一次執行request.getSession()時建立
銷燬:
1)伺服器(非正常)關閉時
2)session過期/失效(預設30分鐘)
問題:時間的起算點 從何時開始計算30分鐘?
從不操作伺服器端的資源開始計時
可以在工程的web.xml中進行配置
右鍵專案名-->java ee tools-->generate deployment discriptior stub
單位是分鐘
<session-config>
<session-timeout>30</session-timeout>
</session-config>
3)手動銷燬session
session.invalidate();
作用範圍:
預設在一次會話中,也就是說在,一次會話中任何資源公用一個session物件
面試題:瀏覽器關閉,session就銷燬了? 不對
瀏覽器關閉時銷燬的是cookie中的sessionid,session還在伺服器中,只是客戶端找不到session了而已
如果想不銷燬sessionid那麼可以對cookie設定持久化時間,把cookie變成檔案形式永久儲存
session、request、和ServletContext的區別?
從作用域,生命週期2方面闡述
https://blog.csdn.net/qq_24341213/article/details/52843831
練習:用servlet實現如圖效果
登陸頁面對應的servlet
package com.wowowo.hw;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.websocket.Session;
/**
* Servlet implementation class LoginServlet
*/
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//把sessionid(cookie裡面存放sessionid)設為10分鐘銷燬
HttpSession session = request.getSession();
Cookie cookie = new Cookie("JSESSIONID", session.getId());
cookie.setMaxAge(10 * 60);
cookie.setPath("/my1022/welcome");
response.addCookie(cookie);
//把表單提交的使用者名稱密碼進行驗證
String uname = request.getParameter("uname");
String upwd = request.getParameter("upwd");
//密碼正確,把使用者名稱放到session內,重定向到歡迎頁面
if ("user".equals(uname) && "123".equals(upwd)) {
session.setAttribute("uname", uname);
response.sendRedirect("/my1022/welcome");
} else {
//密碼錯誤
response.getWriter().write("failed");
}
}
}
歡迎頁面對應的servlet
package com.wowowo.hw;
import java.io.IOException;
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;
@WebServlet("/welcome")
public class WelcomeServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
HttpSession session = request.getSession();
//判斷登陸狀態(登陸狀態就是看session內是否有name屬性)
if (session.getAttribute("uname") != null) {
response.getWriter().write("welcome:" + session.getAttribute("uname"));
} else {
response.sendRedirect("/my1022/login.html");
}
}
}