基于Cookie與Session的Servlet?API會話管理操作
??前面的話??
本篇文章將介紹Servlet中的會話管理操作,前面在介紹HTTP的時候,我們已經(jīng)說了Cookie與Session的基本原理,Cookie相當(dāng)于就診卡,就診卡一刷得到的信息就相當(dāng)于Session,Servlet對Cookie與Session有著很好的支持,可以實現(xiàn)會話的管理操作。
??1.Cookie與Session
??1.1回顧與理解Cookie與Session
通過學(xué)習(xí)HTTP,我們知道HTTP協(xié)議是“無狀態(tài)”協(xié)議,這里的“無狀態(tài)”指的是默認(rèn)情況下 HTTP 協(xié)議的客戶端和服務(wù)器之間的當(dāng)次通信, 和下次通信之間沒有直接的聯(lián)系,但是在實際開發(fā)中是需要建立起來聯(lián)系的,比如一個網(wǎng)站的登錄,在生活中,我們登錄一次網(wǎng)站后,再退出網(wǎng)站后,再次登錄網(wǎng)站,我們會發(fā)現(xiàn)并不需要再次輸入賬號密碼登錄,網(wǎng)站會自動地幫助我們登錄。
為了實現(xiàn)類似這種網(wǎng)站的自動登錄,在用戶第一次輸入賬號密碼登錄的時候,服務(wù)器會創(chuàng)建一個Session會話來保存當(dāng)前用戶的數(shù)據(jù)和信息,和生成一個Cookie,Cookie里面含有該Session里面的一些關(guān)鍵身份信息,服務(wù)器會將這個Cookie作為響應(yīng)給客戶端,客戶端或者說是瀏覽器就會將這個Cookie儲存起來,當(dāng)下一次登錄時,瀏覽器發(fā)送請求的時候就會帶上這個Cookie,服務(wù)器收到請求后會去獲取請求中的Cookie列表,并去查詢服務(wù)器中是否存在對應(yīng)的Session,如果存在就會自動登錄,不用用戶輸入賬號與密碼,否則需要用戶輸入賬號密碼進行登錄。
打個比方來說,去醫(yī)院看病需要先掛號,如果你沒有當(dāng)前醫(yī)院的就診卡,就會為你新辦理一個就診卡,這個就診卡里面含有你的一些關(guān)鍵身份信息,并且會在醫(yī)院的服務(wù)器上新建一個檔案,得到這個就診卡,你就可以在該醫(yī)院的各個科室進行刷卡,如果你之前在這個醫(yī)院有就診記錄,你一刷卡就可以查詢到你所有在當(dāng)前醫(yī)院的就診信息。 上面的這個就診卡就相當(dāng)于這里的Cookie,上面有你最基本的身份信息,在醫(yī)院服務(wù)器上所儲存有關(guān)你的詳細(xì)信息,就相當(dāng)于一個會話,也就是一個Session,當(dāng)然服務(wù)器上不止你一個人的Session,它包含很多用戶的信息。
這個Cookie與Session機制最主要的作用就是用來識別用戶的身份信息。
??1.2Servlet會話管理操作
在HttpServletRequest
類中,可以使用getSession
來獲取或創(chuàng)建會話與getCookies
可以獲取請求中的Cookie列表。
方法 | 描述 |
---|---|
HttpSession getSession() | 在服務(wù)器中獲取會話. 參數(shù)如果為 true, 則當(dāng)不存在會話時新建會話; 參數(shù)如果為 false, 則當(dāng)不存在會話時返回 null |
Cookie[] getCookies() | 返回一個數(shù)組, 包含客戶端發(fā)送該請求的所有的 Cookie 對象. 會自動把Cookie 中的格式解析成鍵值對. |
對于getCookies
方法,由于Cookie能夠任意自定義鍵值對,如果想要獲取一般的鍵值對,可以使用該方法獲取,但是如果想要獲取特殊的鍵值對,如SessionId
,可以通過getSession
直接獲取,沒必要通過getCookies
方法的途徑來獲取,因為getSession
方法它自動幫助我們獲取了SessionId
。
調(diào)用getSession
方法所做的事情:
創(chuàng)建會話:
- 第一步,獲取cookie里面的
sessionId
字段,相當(dāng)于會話標(biāo)識。 - 第二步,判斷是否在服務(wù)器上存在。
- 第三步,如果不存在,則創(chuàng)建一個新的HttpSession對象,并生成一個新的
sessionId
。 - 第四步,接下來就會以新生成的
sessionId
作為key,生成的HttpSession對象作為value,以鍵值對形式儲存到類似與哈希表的結(jié)構(gòu)中。 - 第五步,返回響應(yīng),將
sessionId
通過set-Cookie
字段返回給瀏覽器,這樣瀏覽器就得到了sessionId
,瀏覽器就可以將SessionId保存到Cookie中了。
獲取會話:
- 第一步,獲取cookie里面的
sessionId
字段,相當(dāng)于會話標(biāo)識。 - 第二步,判斷是否在服務(wù)器上存在。
- 第三步,直接查詢HttpServlet對象,找到直接作為響應(yīng)返回給客戶端。
關(guān)于HttpSession 這個對象本質(zhì)上也是一個鍵值對的形式,并且允許程序員在對象中儲存任意的鍵值對數(shù)據(jù),但是key必須是String, value隨意。
HttpSession里面的每一個鍵值對稱為屬性(Attribute),該類中提供了兩個方法可以用來獲取該對象中的屬性和儲存屬性(鍵值對)。
方法 | 描述 |
---|---|
Object getAttribute(String name) | 該方法返回在該 session 會話中具有指定名稱的對象,如果沒有指定名稱的對象,則返回 null. |
void setAttribute(String name, Object value) | 該方法使用指定的名稱綁定一個對象到該 session 會話 |
boolean isNew() | 判定當(dāng)前是否是新創(chuàng)建出的會話 |
Cookie類常用方法 Cookie對象包含了兩個屬性,一個是name
,另一個是value
,而在請求中是以鍵值對的形式儲存的,服務(wù)器收到請求后會將鍵值對形式的cookie轉(zhuǎn)換為Cookie對象。
方法 | 描述 |
---|---|
String getName() | 該方法返回 cookie 的名稱。名稱在創(chuàng)建后不能改變。(這個值是 Set-Cooke 字段設(shè)置給瀏覽器的) |
String getValue() | 該方法獲取與 cookie 關(guān)聯(lián)的值 |
void setValue(String newValue) | 該方法設(shè)置與 cookie 關(guān)聯(lián)的值。 |
在響應(yīng)中添加Cookie 我們可以通過HttpServletResponse
類中的addCookie
方法來在響應(yīng)中添加Cookie
,它會作為HTTP響應(yīng)中set-Cookie
字段來進行表示。
方法 | 描述 |
---|---|
void addCookie(Cookie cookie) | 把指定的 cookie 添加到響應(yīng)中. |
??2.常見案例實現(xiàn)
首先我們先得創(chuàng)建Maven文件,引入依賴,創(chuàng)建必要的路徑,這些過程在之前介紹Servlet使用已經(jīng)詳細(xì)介紹了,這里就不再進行贅述了,我們的重點是前端與后端交互過程的實現(xiàn)。
??2.1登錄邏輯的實現(xiàn)
我們經(jīng)常上網(wǎng)查詢一些網(wǎng)站什么的,很多網(wǎng)站都會讓你先登錄,才能使用其中的一些功能,登錄完成之后,一般都會跳到一個主頁網(wǎng)站,下面我們就來簡單地實現(xiàn)一下這一套邏輯。
第一步,約定前后端接口。 我們需要實現(xiàn)兩套交互邏輯,一是登錄跳轉(zhuǎn),二是獲取主頁。 登錄跳轉(zhuǎn)約定: 約定使用POST請求,響應(yīng)采用302重定向。
獲取主頁約定: 采用GET請求,響應(yīng)返回一個頁面。
第二步,編寫前端交互頁面 我們的重點是來學(xué)習(xí)登錄的邏輯,因此登錄的界面不需要很好看很復(fù)雜,只要能夠有兩個輸入框和一個提交按鈕讓我們輸入賬號密碼就行。目標(biāo)頁面如下:
前面我們約定了登錄的跳轉(zhuǎn)采用post請求,由于場景很簡單,我們直接使用form表單構(gòu)造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
標(biāo)簽的name
屬性就對應(yīng)鍵值對的key
,輸入的內(nèi)容就對應(yīng)鍵值對的value
。
第三步,編寫后端處理代碼
對于登錄跳轉(zhuǎn)頁面post請求處理思路如下:
- 從請求中獲取賬號與密碼。
- 驗證賬號與密碼。
- 如果驗證通過,創(chuàng)建會話,并將
username
數(shù)據(jù)加入到會話中,當(dāng)然還可以加入其它的屬性,比如主頁被訪問的次數(shù),創(chuàng)建好會話后,重定向到主頁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"); //驗證賬戶 //驗證按照正常流程應(yīng)該從數(shù)據(jù)庫讀數(shù)據(jù),但是為了便于演示登錄的邏輯,我們直接將賬號密碼寫死 //假設(shè)正確的賬號與密碼是 zhangsan 123 if ("zhangsan".equals(username) && "123".equals(password)) { //登錄成功 //創(chuàng)建會話,為后續(xù)需登錄的頁面做準(zhǔn)備 HttpSession httpSession = req.getSession(true); httpSession.setAttribute("username", username); //初始情況下設(shè)置登錄次數(shù) httpSession.setAttribute("count", 0); //跳轉(zhuǎn)到目標(biāo)頁面index resp.sendRedirect("index"); } else { //登錄失敗 resp.getWriter().write("登錄失?。?); } } }
獲取主頁的get請求處理思路:
- 獲取會話。
- 取出會話信息,將主頁返回次數(shù)加
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 { //返回一個主頁 //獲取會話,參數(shù)必須是false HttpSession httpSession = req.getSession(false); //取出會話信息 String username = (String) httpSession.getAttribute("username"); Integer cnt = (Integer) httpSession.getAttribute("count"); //訪問次數(shù)加1 cnt++; //寫回到會話中 httpSession.setAttribute("count", cnt); //構(gòu)造頁面。我們簡單構(gòu)造一下就好 resp.setContentType("text/html; charset=utf8"); resp.getWriter().write("<h4>歡迎您!" + username + "</h4> <h4>這個主頁已經(jīng)被訪問了" + cnt + "次</h4>"); } }
抓包結(jié)果:
第一次交互,瀏覽器從服務(wù)器上拿到登錄頁面。 第二次交互,瀏覽器給服務(wù)器一個登錄請求,服務(wù)器返回響應(yīng),重定向頁面。 第三次交互,瀏覽器收到302重定向響應(yīng)后,再次向服務(wù)器發(fā)起請求,訪問主頁。
效果演示:
??2.2上傳文件
上傳文件時,前端需要使用到form表單,表單中需要使用一種特殊的類型,叫做form-data
類型。
提交文件的時候,瀏覽器會把文件以form-data
的格式構(gòu)造到Http請求中,服務(wù)器可以通過getPart
方法來取的Part
對象(文件),再通過Part
對象就能夠獲取到文件信息了。
方法 | 描述 |
---|---|
Part getPart(String name) | 獲取請求中給定 name 的文件 |
Collection getParts() | 獲取所有的文件 |
Part類常用方法:
方法 | 描述 |
---|---|
String getSubmittedFileName() | 獲取提交的文件名 |
String getContentType() | 獲取提交的文件類型 |
long getSize() | 獲取文件的大小 |
void write(String path) | 把提交的文件數(shù)據(jù)寫入磁盤文件 |
上傳文件的請求采用post請求,使用form表單構(gòu)造post請求,我們來實現(xiàn)將一個文件上傳到本機的一個目錄下。
前端交互頁面:
<!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>
后端處理代碼:
基本實現(xiàn)思路:
- 從請求中獲取Part對象。
- 調(diào)用Part對象的一些方法,獲取文件信息,在磁盤寫文件等。
- 返回響應(yīng)。
- 加上@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"); //返回響應(yīng) resp.setContentType("text/html; charset=utf8"); resp.getWriter().write("上傳成功!"); } }
效果演示:
看看我們的目標(biāo)目錄上有沒有上傳的圖片。
發(fā)現(xiàn)我們上傳的文件已經(jīng)拷貝到我們所寫的磁盤位置上了,我們上傳的文件會儲存在響應(yīng)的body
部分,在這個body中,會使用一段隨機的字符串作為標(biāo)志,表示文件的開始與結(jié)束。
以上就是基于Cookie與Session的Servlet API會話管理操作的詳細(xì)內(nèi)容,更多關(guān)于Servlet API會話管理的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
使用mybatis的typeHandler對clob進行流讀寫方式
這篇文章主要介紹了使用mybatis的typeHandler對clob進行流讀寫方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-01-01Spring?Cloud?使用?Resilience4j?實現(xiàn)服務(wù)熔斷的方法
服務(wù)熔斷是為了保護我們的服務(wù),比如當(dāng)某個服務(wù)出現(xiàn)問題的時候,控制打向它的流量,讓它有時間去恢復(fù),或者限制一段時間只能有固定數(shù)量的請求打向這個服務(wù),這篇文章主要介紹了Spring?Cloud?使用?Resilience4j?實現(xiàn)服務(wù)熔斷,需要的朋友可以參考下2022-12-12MyBatis超詳細(xì)講解如何實現(xiàn)分頁功能
MyBatis-Plus?是一個?Mybatis?增強版工具,在?MyBatis?上擴充了其他功能沒有改變其基本功能,為了簡化開發(fā)提交效率而存在,本篇文章帶用它實現(xiàn)分頁功能2022-03-03