Java Web 會話技術總結
會話技術
會話概念
-
一次會話中包含多次請求和響應。
一次會話:瀏覽器第一次給服務器資源發送請求,會話建立,直到有一方斷開為止,一次會話結束。
會話的功能
- 在一次會話的范圍內的多次請求間,共享數據。
會話實現的方式
- 客戶端會話技術:Cookie
- 服務端會話技術:Session
Cookie
- 概念:客戶端會話技術,將數據保存到客戶端
快速入門
使用步驟
-
創建Cookie對象,綁定數據
new Cookie(String name, String value); -
服務端發送Cookie到客戶端
response.addCookie(Cookie cookie); -
客戶端訪問新資源,攜帶Cookie數據
Cookie[] cookies = request.getCookie();
項目實戰
-
案例編碼
-
寫Cookie數據到客戶端的Servlet
package org.taoguoguo.cookie; 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 java.io.IOException; /** * @author taoguoguo * @description Cookie快速入門-寫Cookie數據到客戶端的Servlet * @website http://www.rzrgm.cn/doondo * @create 2020-11-17 21:44 */ @WebServlet("/CookieDemo1") public class CookieDemo1 extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //1.創建Cookie對象 Cookie c = new Cookie("msg","hello"); //2.發送Cookie response.addCookie(c); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doPost(request, response); } } -
獲取客戶端Cookie數據的Servlet
package org.taoguoguo.cookie; 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 java.io.IOException; /** * @author taoguoguo * @description cookie快速入門-獲取客戶端Cookie數據 * @website http://www.rzrgm.cn/doondo * @create 2020-11-17 21:46 */ @WebServlet("/CookieDemo2") public class CookieDemo2 extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //3.獲取Cookie Cookie[] cookies = request.getCookies(); //獲取數據,遍歷Cookie if(cookies!=null){ for(Cookie cookie:cookies){ System.out.println(cookie.getName() + "->" + cookie.getValue()); } } } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doPost(request, response); } } -
輸出結果:
msg->hello
-
Cookie實現原理
基于響應頭 set-cookie和請求頭cookie實現
客戶端請求訪問服務器,服務器將需要存儲到cookie里面的內容,通過鍵值對形式寫到set-cookie頭中,然后將數據響應給客戶端;
客戶端再次發送請求時,會將存儲在客戶端的Cookie數據,放在請求頭cookie中,提交給服務端,服務器可獲取請求頭中Cookie的數據,進行數據,從而做到交互。
Cookie的細節
-
一次是否可以發送多個Cookie?
- 可以,服務端可以創建多個Cookie對象,使用response調用多次addCookie方法發送Cookie即可。
-
Cookie在瀏覽器中的有效時間?
- 默認情況下,當瀏覽器關閉,Cookie數據被銷毀
- 持久化存儲
/*int 取值分類 正數:將Cookie數據寫到硬盤文件中,持久化存儲,Cookie 存活時間(單位為:秒) 負數:默認值 零:刪除Cookie信息 */ setMaxAge(int seconds)package org.taoguoguo.cookie; 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 java.io.IOException; /** * @author taoguoguo * @description Cookie存活時間舉例 * @website http://www.rzrgm.cn/doondo * @create 2020-11-17 21:46 */ @WebServlet("/CookieDemo3") public class CookieDemo3 extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //1.創建Cookie對象 Cookie cookie = new Cookie("msg","setMaxAge"); //2.設置Cookie的存活時間 cookie.setMaxAge(30); //將Cookie持久化硬盤,30后會自動刪除Cookie文件 //3.發送Cookie response.addCookie(cookie); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doPost(request, response); } } -
Cookie是否可以存儲中文
-
Tomcat 8 之前,Cookie中不能直接存儲中文數據
解決方案:將中文轉碼,一般采用URL編碼//存中文到Cookie編碼 Cookie cookie = new Cookie("uname", URLEncoder.encode(uname,"utf-8")); //獲取Cookie解碼 String value = URLDecoder.decode(cookie.getValue(),"utf-8"); -
Tomcat 8 之后,Cookie支持中文數據,但不支持特殊字符,依然可以通過URL編碼解決
-
-
Cookie的獲取范圍多大?
-
假設在一個Tomcat服務器中,部署了多個web項目,那么這些web項目的Cookie能否共享?
-
默認情況下,Cookie不能共享
//設置cookie的取值范圍 默認情況下,設置為當前的虛擬目錄也就是 request.getContext的值 //如果需要共享,需要將path 設置為父級別目錄 //比如 /project1; /project2 兩個項目需要共享,則 setPath("/") c.setPath(String path)
-
-
不同的Tomcat服務器間Cookie共享問題?
-
設置一級域名相同,那么多個服務器之間的Cookie可以共享
/** 比如,貼吧的域名為: tieba.baidu.com 而新聞的域名為 news.baidu.com 其中 baidu.com 為一級域名 tieba、news 等為二級域名 c.setDomain(".baidu.com") 那么tieba.baidu.com 和 news.baidu.com中的Cookie可以共享 */ c.setDomain(String path);
-
-
Cookie的特點和作用
- Cookie存儲數據在客戶端瀏覽器
- 瀏覽器對于單個Cookie的大小限制,以及對同一個域名下的總Cookie數量也有限制(20個)
作用:
-
cookie一般用于存儲少量的不太敏感的數據
-
在不登陸的情況下,服務器對客戶端的身份的識別
比如在百度瀏覽器未登錄情況下做一些偏好設置,服務端發一個Cookie給瀏覽器,瀏覽器訪問時帶上這個Cookie,服務端就可以渲染對應的偏好設置
Cookie案例
需求:1。訪問一個Servlet,如果第一次訪問,則提示:您好,歡迎首次訪問;如果不是第一次訪問,則提示歡迎回來,您上次訪問時間為:顯示時間字符串。
分析:
-
可以采用Cookie來完成
-
在服務器中的Servlet來判斷是否有一個名為 lastTime的Cookie
有:不是第一次訪問
- 響應數據:歡迎回來,你上次訪問的時間為: 獲取lastTime中Cookie數據
- 寫回Cookie, 更新Cookie時間的值
沒有:第一次訪問
- 響應數據,您好,歡迎你首次訪問
- 寫回Cookie: lastTime:當前時間
案例代碼:
package org.taoguoguo.cookie;
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 java.io.IOException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* @author taoguoguo
* @description Cookie案例
* @website http://www.rzrgm.cn/doondo
* @create 2020-11-17 21:46
*/
@WebServlet("/cookieTest")
public class CookieTest extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//設置響應消息體的數據格式和編碼
response.setContentType("text/html;charset=utf-8");
//1.獲取所有Cookie
Cookie[] cookies = request.getCookies();
boolean flag = false; //默認沒有Cookie
//2.遍歷Cookie數組
if(cookies!=null && cookies.length>0){
for (Cookie cookie : cookies) {
//3.獲取cookie的名稱
String name = cookie.getName();
//4.判斷名稱是否為 lastTime
if("lastTime".equals(name)){
flag = true;
//Cookie存在,不是第一次訪問
String value = cookie.getValue();
value = URLDecoder.decode(value,"utf-8");
//獲取當前時間的字符串,重新設置Cookie的值,重新發送Cookie
String str_date = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss").format(new Date());
str_date = URLEncoder.encode(str_date, "utf-8");
cookie.setValue(str_date);
//設置Cookie的存活時間
cookie.setMaxAge(60*60); //一小時
response.addCookie(cookie);
//cookie
response.getWriter().write("<h1>歡迎回來,您上次的訪問時間為: "+value+"</h1>");
}
}
}
if(cookies == null || cookies.length==0 || flag == false){
//沒有 第一次訪問
String str_date = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss").format(new Date());
//時間輸出格式中空格特殊字符處理
str_date = URLEncoder.encode(str_date, "utf-8");
Cookie cookie = new Cookie("lastTime",str_date);
//設置Cookie的存活時間
cookie.setMaxAge(60*60); //一小時
response.addCookie(cookie);
response.getWriter().write("<h1>您好,歡迎你首次訪問</h1>");
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
JSP總結
JSP 概念
Java Server Page: Java服務端頁面,可以理解為一個特殊的頁面,既可以定義html標簽,又可以定義Java代碼,用于簡化書寫。
JSP 原理
JSP 本質就是一個Servlet ,在啟動項目訪問 JSP 頁面時,我們的Tomcat容器空間中,會將JSP 文件 編譯為一個 .java 文件 然后Javac編譯為一個.class文件 提供訪問。我們可以打開 index.jsp 經過編譯 后的 index_jsp.java , 發現其繼承 自 org.apache.jasper.runtime.HttpJspBase,而 HttpJspBase 繼承自 HttpServlet, 所以說 JSP 本質 就是 Servlet
而這個Servlet 的 service 方法 做的事情 就是將我們的返回數據,通過輸出流 拼接html 標簽 ,從而實現頁面展示效果。
JSP 腳本
- <% Java代碼 %>
- 定義的 Java代碼在Jsp 轉換成 java后,位于Service方法中,Service 方法中可以怎么寫,該腳本語法就可以怎么寫。
- <%! Java代碼 %>
- 定義的 Java代碼 在Jsp 轉換成 java后,位于類的成員變量中,但這種一般用得比較少。因為servlet是單例的,多個線程同時訪問,如果值有修改,其他線程獲取時會存在線程安全問題,Servlet中一般推薦定義局部變量。
- <%= Java代碼 %>
- 定義的Java代碼,會輸出到頁面上。
out.print()可以輸出什么,該腳本就可以定義什么
- 定義的Java代碼,會輸出到頁面上。
JSP內置對象
在 JSP 頁面不需要獲取和創建,可以直接使用的對象
JSP一共有9個內置對象:
-
request
-
response
-
out:字符輸出流對象,可以將數據輸出到頁面上。和response.getWriter()類似
-
response.getWriter() 和 out.write 的區別:
tomcat 服務器真正給客戶端做出響應之前,會先找 response 緩沖區數據,再找 out 緩沖區數據。response.getWriter() 數據輸出永遠再 out.write() 之前
-
Session
概念
服務器端的會話技術,在一次會話的多次請求間共享數據,將數據保存在服務器端的對象中,HttpSession
快速入門
-
獲取 HttpSession 對象:
- HttpSession session = request.getSession();
-
使用 HttpSession 對象:
- Object getAttribute(String name);
- void setAttribute(String name, Object value);
- void removeAttribute(String name);
-
案例編碼
-
SessionDemo1設置Session數據
package org.taoguoguo.session; 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; /** * @author taoguoguo * @description ${NAME} * @website http://www.rzrgm.cn/doondo * @create 2020-11-23 23:00 */ @WebServlet("/sessionDemo1") public class SessionDemo1 extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //使用Session共享數據 //1.獲取Session HttpSession session = request.getSession(); //2.存儲數據 session.setAttribute("msg","hello session"); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doPost(request, response); } } -
SessionDemo2獲取Session數據
package org.taoguoguo.session; 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; /** * @author taoguoguo * @description ${NAME} * @website http://www.rzrgm.cn/doondo * @create 2020-11-23 23:00 */ @WebServlet("/sessionDemo2") public class SessionDemo2 extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //使用Session獲取數據 //1.獲取Session HttpSession session = request.getSession(); //2.存儲數據 Object msg = session.getAttribute("msg"); System.out.println(msg); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doPost(request, response); } }
-
Session實現原理
思考SessionDemo1 和 SessionDemo2 是不是同一個Session呢? 答案是肯定的同一個。不然怎么從Session中獲取到數據呢?
Session的實現是依賴于Cookie的,當服務器創建一個Session時,會在內部記錄一個SessionId,而服務器響應數據給客戶端時,會在響應頭中設置Cookie: JseesionId:sessionid值的方式,將創建的SessionId 發放到客戶端。而客戶端再次請求服務器時,會將客戶端的Cookie:JessionId 發送到服務端,服務端根據這個SessionId,就能獲取到對應ID的Session對象,拿到會話中的數據。
Session的細節
-
當客戶端關閉后,服務器不關閉,兩次獲取的Session是否為同一個?
-
默認情況下,不是。因為一次新的會話,從request中獲取的Session對象的SessionId是不一樣的。
-
如果需要相同,可以通過創建Cookie,鍵為 JESSIONID,設置最大存活時間,讓Cookie持久化的方法來實現。因為Session是基于Cookie來是實現的,只要Cookie每次攜帶的JessionId相同,就是同一個Session。
實例代碼:
package org.taoguoguo.session; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.*; import java.io.IOException; /** * @author taoguoguo * @description ${NAME} * @website http://www.rzrgm.cn/doondo * @create 2020-11-23 23:00 */ @WebServlet("/sessionDemo3") public class SessionDemo3 extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //使用Session獲取數據 //1.獲取Session HttpSession session = request.getSession(); System.out.println(session); //期望客戶端關閉后,session也能相同 Cookie cookie = new Cookie("JESSIONID",session.getId()); cookie.setMaxAge(60*60); response.addCookie(cookie); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doPost(request, response); } }
-
-
客戶端不關閉,服務器關閉,兩次獲取的Session是同一個嗎?
不是同一個,因為Session對象是由服務器創建的,服務器關閉,Session對象就內存就清空了。重啟后新創建的Session的地址值是隨機分配的。
但現在有個場景,假設用戶在基于Session實現的購物車中,添加了N件商品,但準備下單時接到電話。接電話過程中,服務器重啟。待電話結束后,點擊下單,Session由于不是同一個對象數據丟失了該如何處理?
- Session的鈍化:
- 在服務器正常關閉之前,將Session對象序列化到硬盤上
- Session的活化:
- 在服務器啟動后,將Session文件轉換為內存中的Session對象即可
服務器Tomcat已經實現了Session的鈍化和活化。將項目打成 war 包部署到Tomcat的 webapp 目錄下,正常啟動Tomcat,訪問存儲Session的Servlet, 當Tomcat正常關閉時,Tomcat 的 work 目錄會產生一個
SESSIONS.ser的文件,持久化Session的數據,當Tomcat再次啟動時,會將SESSION.ser文件數據加載到Tomcat內存中序列化為Session對象,從而拿到Session存儲的數據。但IDEA 編輯器只實現的Session的鈍化,沒有實現Session的活化。IDEA在啟動Tomcat時,會先刪除 work 目錄,然后新建work 目錄,獲取不到
Session.ser文件,及其存儲的數據。 - Session的鈍化:
-
Session什么時候被銷毀?
-
服務器關閉
-
session 對象調用 invalidate() 方法
-
session的默認失效時間為30分鐘
Tomcat 的 web.xml 選擇性配置 設置Session時長:
<session-config> <session-timeout>30</session-timeout> </session-config>
-
Session的特點
- Session用于存儲一次會話的多次請求的數據,存在服務器端。
- Session可以存儲任意類型,任意大小的數據。
Session與Cookie的區別
- Session 存儲數據在服務端,Cookie在客戶端
- Session沒有數據大小限制,Cookie有大小限制
- Session數據存在服務端較為安全,Cookie相對不安全
Session案例
案例需求
訪問帶有驗證碼的登錄頁面,用戶輸入用戶名,密碼,及驗證碼
- 如果用戶名和密碼輸入有誤,則跳轉至登錄頁面,提示:用戶名或密碼錯誤
- 如果驗證碼錯誤,跳轉登錄頁面,提示:驗證碼錯誤
- 如果全部輸入正確,則跳轉至主頁 success.jsp 顯示:用戶名,歡迎您
案例編碼:
-
驗證碼Servlet
package org.taoguoguo.servlet; import javax.imageio.ImageIO; 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 java.awt.*; import java.awt.image.BufferedImage; import java.io.IOException; import java.util.Random; /** * @author taoguoguo * @description ${NAME} * @website http://www.rzrgm.cn/doondo * @create 2020-11-24 23:33 */ @WebServlet("/checkCodeServlet") public class CheckCodeServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //1.創建一個圖像緩沖區對象 int width = 100; int height = 50; BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR); //2.美化圖片 //2.1填充背景色 Graphics graphics = image.getGraphics(); //畫筆對象 graphics.setColor(Color.pink); //設置畫筆顏色 graphics.fillRect(0,0,width,height); //2.2畫邊框 graphics.setColor(Color.blue); graphics.drawRect( 0,0,width-1,height-1); //2.3寫驗證碼 String code = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz123456789"; //生成隨機角標 Random random = new Random(); StringBuilder builder = new StringBuilder(); for (int i=1; i<=4; i++){ int index = random.nextInt(code.length()); //獲取字符 char c = code.charAt(index); builder.append(c); graphics.drawString(String.valueOf(c),width/5*i,height/2); } //將驗證碼存入到Session String checkCode_session = builder.toString(); request.getSession().setAttribute("checkCode_session",checkCode_session); //2.4畫干擾線 graphics.setColor(Color.green); for(int i=0; i<10; i++){ int x1 = random.nextInt(width); int y1 = random.nextInt(height); int x2 = random.nextInt(width); int y2 = random.nextInt(height); graphics.drawLine(x1,y1,x2,y2); } //3.將圖片輸出到頁面展示 ImageIO.write(image,"jpg",response.getOutputStream()); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doPost(request, response); } } -
login.jsp
<%-- User: taoGuoGuo Date: 2020/11/24 Time: 23:36 WebSite: http://www.rzrgm.cn/doondo/ --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>login</title> <script> window.onload = function () { document.getElementById("img").onclick = function () { this.src="/checkCodeServlet?time="+ new Date().getTime(); } } </script> <style> div{ color: red; } </style> </head> <body> <form action="/loginServlet" method="post"> <table> <tr> <td>用戶名</td> <td><input type="text" name="username"></td> </tr> <tr> <td>密碼</td> <td><input type="password" name="password"></td> </tr> <tr> <td>驗證碼</td> <td><input type="text" name="checkCode"></td> </tr> <tr> <td colspan="2"><img src="/checkCodeServlet" id="img"></td> </tr> <tr> <td colspan="2"><input type="submit" value="登錄"></td> </tr> </table> </form> <div><%=request.getAttribute("errorMsg") == null ? "": request.getAttribute("errorMsg")%></div> </body> </html> -
loginServlet
package org.taoguoguo.servlet; 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; import java.util.Map; /** * @author taoguoguo * @description ${NAME} * @website http://www.rzrgm.cn/doondo * @create 2020-11-24 23:47 */ @WebServlet("/loginServlet") public class LoginServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //1.設置request編碼 request.setCharacterEncoding("utf-8"); //2.獲取參數 String username = request.getParameter("username"); String password = request.getParameter("password"); String checkCode = request.getParameter("checkCode"); //3.先獲取生成的驗證碼 HttpSession session = request.getSession(); String checkCode_session = session.getAttribute("checkCode_session").toString(); //刪除Session中存儲的驗證碼 session.removeAttribute("checkCode_session"); //4.先判斷驗證碼是否正確 if (checkCode_session != null && checkCode_session.equalsIgnoreCase(checkCode)){ //忽略大小寫比較 //驗證碼正確 //判斷用戶名和密碼是否一致 if("zhangsan".equals(username) && "123".equals(password)){//需要查詢數據庫 //登錄成功 //存儲用戶信息,重定向到 success.jsp session.setAttribute("user",username); response.sendRedirect(request.getContextPath()+"/success.jsp"); }else{ //存儲信息到request request.setAttribute("errorMsg","用戶名或密碼錯誤"); request.getRequestDispatcher("/login.jsp").forward(request,response); } }else{ //驗證碼不一致 //存儲信息到request request.setAttribute("errorMsg","驗證碼錯誤"); request.getRequestDispatcher("/login.jsp").forward(request,response); } } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doPost(request, response); } } -
success.jsp
<%-- User: taoGuoGuo Date: 2020/11/25 Time: 0:15 WebSite: http://www.rzrgm.cn/doondo/ --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>success</title> </head> <body> <h1><%=request.getSession().getAttribute("user")%>,歡迎您</h1> </body> </html>

浙公網安備 33010602011771號