开发中经常碰到的问题cookie和session问题,今天一并解决
點擊上方“好好學(xué)java”,選擇“置頂公眾號”
優(yōu)秀學(xué)習(xí)資源、干貨第一時間送達(dá)!
?精彩內(nèi)容?
java實戰(zhàn)練習(xí)項目教程
2018微服務(wù)資源springboot、springcloud、docker、dubbo實戰(zhàn)等傾心分享
2018年java架構(gòu)師全套學(xué)習(xí)教程
最新大數(shù)據(jù)培訓(xùn)完整視頻教程
2018年java最新全套培訓(xùn)學(xué)習(xí)教程
一、會話的概念
會話可簡單理解為:用戶開一個瀏覽器,點擊多個超鏈接,訪問服務(wù)器多個web資源,然后關(guān)閉瀏覽器,整個過程稱之為一個會話。
二、會話過程中要解決的一些問題
每個用戶在使用瀏覽器與服務(wù)器進(jìn)行會話的過程中,不可避免各自會產(chǎn)生一些數(shù)據(jù),程序要想辦法為每個用戶保存這些數(shù)據(jù)。
三、保存會話數(shù)據(jù)的兩種技術(shù)
1、Cookie
Cookie意為"甜餅",是由W3C組織提出,最早由Netscape社區(qū)發(fā)展的一種機(jī)制。目前Cookie已經(jīng)成為標(biāo)準(zhǔn),所有的主流瀏覽器如IE、Netscape、Firefox、Opera等都支持Cookie。
由于HTTP是一種無狀態(tài)的協(xié)議,服務(wù)器單從網(wǎng)絡(luò)連接上無從知道客戶身份。怎么辦呢?就給客戶端們頒發(fā)一個通行證吧,每人一個,無論誰訪問都必須攜帶自己通行證。這樣服務(wù)器就能從通行證上確認(rèn)客戶身份了。這就是Cookie的工作原理。
Cookie實際上是一小段的文本信息。客戶端請求服務(wù)器,如果服務(wù)器需要記錄該用戶狀態(tài),就使用response向客戶端瀏覽器頒發(fā)一個Cookie。客戶端瀏覽器會把Cookie保存起來。當(dāng)瀏覽器再請求該網(wǎng)站時,瀏覽器把請求的網(wǎng)址連同該Cookie一同提交給服務(wù)器。服務(wù)器檢查該Cookie,以此來辨認(rèn)用戶狀態(tài)。服務(wù)器還可以根據(jù)需要修改Cookie的內(nèi)容。
2、Session
Session是服務(wù)器端技術(shù),利用這個技術(shù),服務(wù)器在運行時可以為每一個用戶的瀏覽器創(chuàng)建一個其獨享的session對象,由于session為用戶瀏覽器獨享,所以用戶在訪問服務(wù)器的web資源時,可以把各自的數(shù)據(jù)放在各自的session中,當(dāng)用戶再去訪問服務(wù)器中的其它web資源時,其它web資源再從用戶各自的session中取出數(shù)據(jù)為用戶服務(wù)。
四、Cookie類的主要方法
| void setMaxAge(intexpiry) | 以秒計算,設(shè)置Cookie過期時間。 |
| String getName() | 返回Cookie的名字。名字和值是我們始終關(guān)心的兩個部分,筆者會在后面詳細(xì)介紹 getName/setName。 |
| void setValue(String newValue) | Cookie創(chuàng)建后設(shè)置一個新的值。 |
| String getValue() | 返回Cookie的值。筆者也將在后面詳細(xì)介紹getValue/setValue。 |
| void setDomain(String pattern) | 設(shè)置cookie中Cookie適用的域名 |
| String getDomain() | 返回Cookie中Cookie適用的域名. 使用getDomain() 方法可以指示瀏覽器把Cookie返回給同 一域內(nèi)的其他服務(wù)器,而通常Cookie只返回給與發(fā)送它的服務(wù)器名字完全相同的服務(wù)器。注意域名必須以點開始(例如.yesky.com) |
| void setPath(String uri) | 指定Cookie適用的路徑。 |
| String getPath() | 返回Cookie適用的路徑。如果不指定路徑,Cookie將返回給當(dāng)前頁面所在目錄及其子目錄下 的所有頁面。 |
| void setSecure(boolean flag) | 指出瀏覽器使用的安全協(xié)議,例如HTTPS或SSL。 |
| boolean getSecure() | 如果瀏覽器通過安全協(xié)議發(fā)送cookies將返回true值,如果瀏覽器使用標(biāo)準(zhǔn)協(xié)議則返回false值。 |
| void setVersion(int v) | Cookie所遵從的協(xié)議版本。 |
| int getVersion() | 返回Cookie所遵從的協(xié)議版本。 |
| void setComment(String purpose) | 設(shè)置cookie中注釋。 |
| String getComment() | 返回Cookie中注釋,如果沒有注釋的話將返回空值。 |
| Cookie(String name, String value) | 實例化Cookie對象,傳入cooke名稱和cookie的值。 |
response接口也中定義了一個addCookie方法,它用于在其響應(yīng)頭中增加一個相應(yīng)的Set-Cookie頭字段。 同樣,request接口中也定義了一個getCookies方法,它用于獲取客戶端提交的Cookie。
五、Cookie使用
1、使用cookie記錄用戶上一次訪問的時間
public?class?CookieDemo?extends?HttpServlet{private?static?final?long?serialVersionUID?=?5757885987685925915L;@Overrideprotected?void?doGet(HttpServletRequest?req,?HttpServletResponse?resp)?throws?ServletException,?IOException?{doPost(req,?resp);}@Overrideprotected?void?doPost(HttpServletRequest?req,?HttpServletResponse?resp)?throws?ServletException,?IOException?{//設(shè)置服務(wù)器以UTF-8編碼輸出resp.setCharacterEncoding("UTF-8");//設(shè)置瀏覽器以UTF-8編碼進(jìn)行接收resp.setContentType("text/html;charset=UTF-8");PrintWriter?out?=?resp.getWriter();//獲取Cookie數(shù)組Cookie[]?cookie?=?req.getCookies();if(cookie?==?null){out.write("這是你的第一次訪問!");}?else?{for?(Cookie?ck?:?cookie)?{if(ck.getName().equals("cookieName")){//獲取Cookie里面保存的數(shù)據(jù)Long?time?=?Long.parseLong(ck.getValue());Date?date?=?new?Date(time);out.write("上次訪問時間:"?+?date.toLocaleString());}}}//創(chuàng)建一個cookie,cookie的名字是cookieNameCookie?cookies?=?new?Cookie("cookieName",?System.currentTimeMillis()+"");//設(shè)置Cookie的有效期為1天,這樣即使關(guān)閉了瀏覽器,下次再訪問時,也依然可以通過cookie獲取用戶上一次訪問的時間。cookies.setMaxAge(24*60*60);//將cookie對象添加到response對象中resp.addCookie(cookies);} }第一次訪問時,如下所示:
image再次訪問:
image2、刪除Cookie
public?class?CookieDemo?extends?HttpServlet?{@Overrideprotected?void?doGet(HttpServletRequest?req,?HttpServletResponse?resp)throws?ServletException,?IOException?{doPost(req,?resp);}@Overrideprotected?void?doPost(HttpServletRequest?req,?HttpServletResponse?resp)throws?ServletException,?IOException?{//創(chuàng)建一個名字為lastAccessTime的cookieCookie?cookie?=?new?Cookie("lastAccessTime",?System.currentTimeMillis()+"");//將cookie的有效期設(shè)置為0,命令瀏覽器刪除該cookiecookie.setMaxAge(0);req.addCookie(cookie);} }3、cookie中存/取中文
public?class?CookieDemo2?extends?HttpServlet{private?static?final?long?serialVersionUID?=?5757885987685925915L;@Overrideprotected?void?doGet(HttpServletRequest?req,?HttpServletResponse?resp)?throws?ServletException,?IOException?{doPost(req,?resp);}@Overrideprotected?void?doPost(HttpServletRequest?req,?HttpServletResponse?resp)?throws?ServletException,?IOException?{//設(shè)置服務(wù)器以UTF-8編碼輸出resp.setCharacterEncoding("UTF-8");//設(shè)置瀏覽器以UTF-8編碼進(jìn)行接收resp.setContentType("text/html;charset=UTF-8");PrintWriter?out?=?resp.getWriter();//創(chuàng)建一個cookie,cookie的名字是cookieName//存儲中文時,使用URLEncoder類里面的encode(String?s,?String?enc)方法進(jìn)行中文轉(zhuǎn)碼Cookie?cookies?=?new?Cookie("cookieName",?URLEncoder.encode("哎喲!不錯喲",?"UTF-8"));//將cookie對象添加到response對象中resp.addCookie(cookies);//獲取Cookie數(shù)組Cookie[]?cookie?=?req.getCookies();if(cookie?!=?null){for?(Cookie?ck?:?cookie)?{if(ck.getName().equals("cookieName")){//獲取Cookie里面保存的數(shù)據(jù)String?text?=?ck.getValue();//使用URLDecoder類里面的decode(String?s,?String?enc)進(jìn)行解碼out.write("存儲的中文數(shù)據(jù):"?+?URLDecoder.decode(text,?"UTF-8"));}}}} }結(jié)果如下:
imageCookie注意細(xì)節(jié)
1,一個Cookie只能標(biāo)識一種信息,它至少含有一個標(biāo)識該信息的名稱(NAME)和設(shè)置值(VALUE)。
2,一個WEB站點可以給一個WEB瀏覽器發(fā)送多個Cookie,一個WEB瀏覽器也可以存儲多個WEB站點提供的Cookie。
3,瀏覽器一般只允許存放300個Cookie,每個站點最多存放20個Cookie,每個Cookie的大小限制為4KB。
4,如果創(chuàng)建了一個cookie,并將他發(fā)送到瀏覽器,默認(rèn)情況下它是一個會話級別的cookie(即存儲在瀏覽器的內(nèi)存中),用戶退出瀏覽器之后即被刪除。若希望瀏覽器將該cookie存儲在磁盤上,則需要使用maxAge,并給出一個以秒為單位的時間。將最大時效設(shè)為0則是命令瀏覽器刪除該cookie。
六、Session簡單介紹
在WEB開發(fā)中,服務(wù)器可以為每個用戶瀏覽器創(chuàng)建一個會話對象(session對象),注意:一個瀏覽器獨占一個session對象(默認(rèn)情況下)。因此,在需要保存用戶數(shù)據(jù)時,服務(wù)器程序可以把用戶數(shù)據(jù)寫到用戶瀏覽器獨占的session中,當(dāng)用戶使用瀏覽器訪問其它程序時,其它程序可以從用戶的session中取出該用戶的數(shù)據(jù),為用戶服務(wù)。
Session和Cookie的主要區(qū)別
1,Cookie是把用戶的數(shù)據(jù)寫給用戶的瀏覽器。
2,Session技術(shù)把用戶的數(shù)據(jù)寫到用戶獨占的session中。
3,Session對象由服務(wù)器創(chuàng)建,開發(fā)人員可以調(diào)用request對象的getSession方法得到session對象。
七、Session基礎(chǔ)知識
Session是服務(wù)器端技術(shù),利用這個技術(shù),服務(wù)器在運行時可以為每一個用戶的瀏覽器創(chuàng)建一個其獨享的session對象,由于session為用戶瀏覽器獨享,所以用戶在訪問服務(wù)器的web資源時,可以把各自的數(shù)據(jù)放在各自的session中,當(dāng)用戶再去訪問服務(wù)器中的其它web資源時,其它web資源再從用戶各自的session中取出數(shù)據(jù)為用戶服務(wù)。
當(dāng)用戶打開瀏覽器,訪問某個網(wǎng)站操作session時,服務(wù)器就會在服務(wù)器的內(nèi)存為該瀏覽器分配一個session對象,該session對象被這個瀏覽器獨占。
這個session對象也可以看做是一個容器,session默認(rèn)存在時間為30min,你可以修改。
1、Session可以用來做什么
1、網(wǎng)上商城中的購物車
2、保存登錄用戶的信息
3、將某些數(shù)據(jù)放入到Session中,供同一用戶的各個頁面使用
4、防止用戶非法登錄到某個頁面。
2、Session基本使用
| request.getSession(boolean create) | 返回這個request綁定的session對象,如果沒有,則根據(jù)create的值決定是否創(chuàng)建一個 |
| session.setAttribute(String name,Object val) | 向session中添加屬性 |
| session.getAttribute(String name) | 從session中得到某個屬性 |
| session.removeAttribute(String name) | 從session中刪除某個屬性 |
| session.setMaxInactiveInterval() | 設(shè)置Session的生命周期(單位秒),Session的生命周期默認(rèn)是30min |
| session.invalidate() | 清除所有session |
| session.removeAttribute(String name) | 刪除指定名稱的session |
Servlet1:
public?class?Servlet1?extends?HttpServlet?{private?static?final?long?serialVersionUID?=?-8236507185410764108L;@Overrideprotected?void?doGet(HttpServletRequest?req,?HttpServletResponse?resp)?throws?ServletException,?IOException?{doPost(req,?resp);}@Overrideprotected?void?doPost(HttpServletRequest?req,?HttpServletResponse?resp)?throws?ServletException,?IOException?{//獲取sessionHttpSession?session?=?req.getSession();//將數(shù)據(jù)存儲到session中session.setAttribute("name",?"Zender");} }Servlet2:
public?class?Servlet2?extends?HttpServlet?{private?static?final?long?serialVersionUID?=?-8236507185410764108L;@Overrideprotected?void?doGet(HttpServletRequest?req,?HttpServletResponse?resp)?throws?ServletException,?IOException?{doPost(req,?resp);}@Overrideprotected?void?doPost(HttpServletRequest?req,?HttpServletResponse?resp)?throws?ServletException,?IOException?{//獲取sessionHttpSession?session?=?req.getSession();//獲取nameString?name?=?(String)?session.getAttribute("name");PrintWriter?out?=?resp.getWriter();out.print("name:"?+?name);} }同一瀏覽器訪問Servlet1,再訪問Servlet2,結(jié)果如下:
image不同瀏覽器訪問Servlet1,再訪問Servlet2,結(jié)果如下:
image可以看到這時候name是null,也就是沒有從session對象中取出值,因為360瀏覽器并沒有運行Servlet1來創(chuàng)建Session對象,上面的session對象是Chrome瀏覽器獨占的。
3、Session生命周期
Session中的屬性的默認(rèn)生命周期是30min,這個默認(rèn)時間可以通過修改web.xml文件來修改
1,在Tomcat根目錄\conf\web.xml文件中修改
<session-config><session-timeout>30</session-timeout></session-config>2,如果只需要對某一個web應(yīng)用設(shè)置,則只需要修改對應(yīng)web應(yīng)用的web.xml文件。在這個web.xml文件中添加如上的代碼:
<session-config><session-timeout>10</session-timeout></session-config>除了設(shè)置默認(rèn)生命周期之外,最重要的是在程序中設(shè)置,調(diào)用setMaxInacttiveInterval(int interval),這里的interval是以秒為單位的,而且這個方法設(shè)置的是發(fā)呆時間,比如你設(shè)置的是60秒,那么在這60秒之內(nèi)如果你沒有操作過session,它就會自動刪除,如果你操作過,不管是設(shè)置屬性還是讀取屬性,它都會從頭開始計時。
session.setMaxInactiveInterval(60);八、Session實現(xiàn)原理
服務(wù)器是如何實現(xiàn)一個session為一個用戶瀏覽器服務(wù)的?
image1,瀏覽器A先訪問Servlet1,這時候它創(chuàng)建了一個Session,ID號為110,然后Servlet1將這個ID號以Cookie的方式返回給瀏覽器A。
2,瀏覽器A繼續(xù)訪問Servlet2,那么這個請求會帶上Cookie值: JSESSIONID=110,然后服務(wù)器根據(jù)瀏覽器A傳遞過來的ID號找到內(nèi)存中的這個Session。
3,瀏覽器B來訪問Servlet1了,它的請求并沒有帶上 JSESSIONID這個Cookie值,由于它也要使用Session,所以服務(wù)器會新創(chuàng)建一個Session,ID號為111。
4,瀏覽器B繼續(xù)訪問Servlet2,那么這個請求會帶上Cookie值: JSESSIONID=111,然后服務(wù)器根據(jù)瀏覽器B傳遞過來的ID號找到內(nèi)存中的這個Session。
例如:
Servlet1:
public?class?Servlet1?extends?HttpServlet?{private?static?final?long?serialVersionUID?=?-8236507185410764108L;@Overrideprotected?void?doGet(HttpServletRequest?req,?HttpServletResponse?resp)?throws?ServletException,?IOException?{doPost(req,?resp);}@Overrideprotected?void?doPost(HttpServletRequest?req,?HttpServletResponse?resp)?throws?ServletException,?IOException?{//獲取sessionHttpSession?session?=?req.getSession();//將數(shù)據(jù)存儲到session中session.setAttribute("name",?"Zender");PrintWriter?out?=?resp.getWriter();out.print("create?Session?OK");} }Servlet2:
public?class?Servlet2?extends?HttpServlet?{private?static?final?long?serialVersionUID?=?-8236507185410764108L;@Overrideprotected?void?doGet(HttpServletRequest?req,?HttpServletResponse?resp)?throws?ServletException,?IOException?{doPost(req,?resp);}@Overrideprotected?void?doPost(HttpServletRequest?req,?HttpServletResponse?resp)?throws?ServletException,?IOException?{//獲取sessionHttpSession?session?=?req.getSession();//獲取nameString?name?=?(String)?session.getAttribute("name");PrintWriter?out?=?resp.getWriter();out.println("Session?ID:"?+?session.getId());out.print("name:"?+?name);} }第一次訪問Servlet1時,服務(wù)器會創(chuàng)建一個新的sesion,并且把session的Id以cookie的形式發(fā)送給客戶端瀏覽器,如下圖所示:
image可以看到,Request Headers中并沒有Cookie的信息,而Response Headers中有這么一句話:
Set-Cookie:JSESSIONID=05A94199DDC64311563740CC2C78D656;?Path=/CookieAndSession/;?HttpOnly說明這個時候服務(wù)器向客戶端通過Cookie傳遞回了 JSESSIONID這個屬性。
然后訪問Servlet2,如下圖所示:
image可以看到Response Headers中沒有出現(xiàn)Set-Cookie這個頭,而Request Headers中帶上了Cookie這個頭:
Cookie:JSESSIONID=05A94199DDC64311563740CC2C78D656而這個頭中正包含 JSESSIONID,并且它的值也就是我們之前Set-Cookie中 JSESSIONID的值。
這就證明了我們之前圖解的Session的原理,也就是服務(wù)器能夠為不同的瀏覽器區(qū)分不同的Session的機(jī)制。
九、Session的簡單應(yīng)用
1,用戶登錄時候驗證驗證碼
Index.jsp:
<head> <base?href="<%=basePath%>"?/> <meta?http-equiv="Content-Type"?content="text/html;charset=UTF-8"> <title>表單提交</title> <link?href="css/bootstrap.css"?rel="stylesheet"> <script?src="js/jquery-3.2.1.js"></script> <script?src="js/bootstrap.js"></script> </head> <body><form?class="form-horizontal"?action="<%=request.getContextPath()%>/CodeServlet.html"?role="form"?method="post"><div?class="form-group"><label?for="firstname"?class="col-sm-1?control-label">驗證碼</label><div?class="col-sm-3"><input?type="text"?class="form-control"?name="idCodeNum"placeholder="請輸入驗證碼"></div><img?src="idCode"?onclick="this.src+=''"?style="cursor:pointer;"?width="115"?height="30"?title="看不清?換一個"></div><div?class="form-group"><div?class="col-sm-offset-1?col-sm-3"><button?type="submit"?class="btn?btn-default">提交</button><button?type="reset"?class="btn?btn-default">重置</button></div></div></form> </body> </html>CodeServlet:
public?class?CodeServlet?extends?HttpServlet?{private?static?final?long?serialVersionUID?=?-8236507185410764108L;@Overrideprotected?void?doGet(HttpServletRequest?req,?HttpServletResponse?resp)?throws?ServletException,?IOException?{doPost(req,?resp);}@Overrideprotected?void?doPost(HttpServletRequest?req,?HttpServletResponse?resp)?throws?ServletException,?IOException?{//獲取sessionHttpSession?session?=?req.getSession();//獲取存儲的驗證碼String?idCodeText?=?(String)?session.getAttribute("idCode");System.out.println("服務(wù)端驗證碼:"?+?idCodeText);String?idCodeNum?=?req.getParameter("idCodeNum");System.out.println("輸入的驗證碼:"?+?idCodeNum);//設(shè)置瀏覽器以UTF-8編碼進(jìn)行接收resp.setContentType("text/html;charset=UTF-8");PrintWriter?out?=?resp.getWriter();if(idCodeText.equalsIgnoreCase(idCodeNum)){out.println("驗證成功!");}?else?{out.println("驗證碼錯誤!");}} }Web.xml:
<?xml?version="1.0"?encoding="UTF-8"?> <web-app?xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns="http://java.sun.com/xml/ns/javaee"xsi:schemaLocation="http://java.sun.com/xml/ns/javaee?http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"id="WebApp_ID"?version="3.0"><display-name>CookieAndSession</display-name><welcome-file-list><welcome-file>index.html</welcome-file></welcome-file-list><servlet><servlet-name>CodeServlet</servlet-name><servlet-class>com.zender.session.CodeServlet</servlet-class></servlet><servlet-mapping><servlet-name>CodeServlet</servlet-name><url-pattern>/CodeServlet.html</url-pattern></servlet-mapping><!--?生成驗證碼的Servlet?--><servlet><servlet-name>ValidateCode</servlet-name><servlet-class>org.jelly.image.ValidateCode</servlet-class></servlet><servlet-mapping><servlet-name>ValidateCode</servlet-name><url-pattern>/idCode</url-pattern></servlet-mapping> </web-app>這里使用了jelly-core-1.7.0.GA.jar來生成了驗證碼,具體使用方式請點擊:jelly-core-1.7.0.GA.jar(http://www.blogjava.net/fancydeepin/archive/2014/08/03/jelly_image.html)
訪問http://localhost:8081/CookieAndSession/index.jsp輸入驗證碼,點擊提交:
image輸入正確驗證碼,頁面響應(yīng)結(jié)果:
image輸入錯誤驗證碼,頁面響應(yīng)結(jié)果:
image輸入正確驗證碼,后臺結(jié)果:
image輸入錯誤驗證碼,后臺結(jié)果:
image2,實現(xiàn)簡易購物車
模擬一個數(shù)據(jù)庫:
public?class?DB?{private?static?HashMap<String,?Book>?hm?=?null;private?DB(){}static{hm?=?new?LinkedHashMap<String,?Book>();Book?book1?=?new?Book(1,?"Java基礎(chǔ)");Book?book2?=?new?Book(2,?"Oracle數(shù)據(jù)庫");Book?book3?=?new?Book(3,?"C語言");Book?book4?=?new?Book(4,?"Python核心教程");Book?book5?=?new?Book(5,?"Web技術(shù)");hm.put(book1.getId()?+?""?,?book1);hm.put(book2.getId()?+?""?,?book2);hm.put(book3.getId()?+?""?,?book3);hm.put(book4.getId()?+?""?,?book4);hm.put(book5.getId()?+?""?,?book5);}/***?得到數(shù)據(jù)庫中所有的書*?@return*/public?static?HashMap<String,?Book>?getBooks(){return?hm;}/***?根據(jù)ID得到書*?@param?id*?@return*/public?static?Book?getBookById(String?id){if(hm.containsKey(id)){return?hm.get(id);}return?null;} }BuyBookServlet這個Servlet用于購買圖書:
//顯示購買的圖書 public?class?ShowMyCartServlet?extends?HttpServlet?{private?static?final?long?serialVersionUID?=?-8236507185410764108L;@Overrideprotected?void?doGet(HttpServletRequest?req,?HttpServletResponse?resp)?throws?ServletException,?IOException?{doPost(req,?resp);}@Overrideprotected?void?doPost(HttpServletRequest?req,?HttpServletResponse?resp)?throws?ServletException,?IOException?{resp.setContentType("text/html;charset=UTF-8");PrintWriter?out?=?resp.getWriter();HttpSession?session?=?req.getSession();List<Book>?list?=?(ArrayList<Book>)?session.getAttribute("books");if(list==null?||?list.size()==0){out.write("對不起,您還沒有購買任何商品!!");return;}//顯示用戶買過的商品out.write("您買過如下商品:<br/>");for(Book?book?:?list){out.write(book.getName()?+?"<br/>");}} }運行結(jié)果:
image3,防止用戶非法登錄到某個頁面
比如我們的用戶管理系統(tǒng),必須要登錄成功后才能跳轉(zhuǎn)到主頁面,而不能直接繞過登錄頁面直接到主頁面,這個應(yīng)用是一個非常常見的應(yīng)用。
當(dāng)在驗證用戶的控制器LoginClServlet.java驗證用戶成功后,將當(dāng)前的用戶信息保存在Session對象中:
//?把user對象保存在sessionHttpSession?session?=?req.getSession();session.setAttribute("loginUser",?user);然后在主頁面Main.java最開始的地方,取出Session中的登錄用戶信息,如果信息為空,則為非法訪問,直接跳轉(zhuǎn)到登錄頁面,并提示相關(guān)信息:
//?取出login-user這個session User?loginUser?=?(User)req.getSession().getAttribute("loginUser"); if(loginUser?==?null){//?說明用戶沒有登錄,讓他跳轉(zhuǎn)到登錄頁面req.setAttribute("error",?"請登錄!");req.getRequestDispatcher("/LoginServlet").forward(req,res);return; }那么這里就存在一個問題,一個網(wǎng)站會有很多個需要防止非法訪問的頁面,如果都是用這種方法豈不是很麻煩?
兩種解決辦法:
第一種:將這段驗證用戶的代碼封裝成函數(shù),每次調(diào)用
第二種:使用過濾器
4,利用Session防止表單重復(fù)提交
具體的做法:
在服務(wù)器端生成一個唯一的隨機(jī)標(biāo)識號,專業(yè)術(shù)語稱為Token(令牌),同時在當(dāng)前用戶的Session域中保存這個Token。然后將Token發(fā)送到客戶端的Form表單中,在Form表單中使用隱藏域來存儲這個Token,表單提交的時候連同這個Token一起提交到服務(wù)器端,然后在服務(wù)器端判斷客戶端提交上來的Token與服務(wù)器端生成的Token是否一致,如果不一致,那就是重復(fù)提交了,此時服務(wù)器端就可以不處理重復(fù)提交的表單。如果相同則處理表單提交,處理完后清除當(dāng)前用戶的Session域中存儲的標(biāo)識號。
在下列情況下,服務(wù)器程序?qū)⒕芙^處理用戶提交的表單請求:
1,存儲Session域中的Token(令牌)與表單提交的Token(令牌)不同。
2,當(dāng)前用戶的Session中不存在Token(令牌)。
3,用戶提交的表單數(shù)據(jù)中沒有Token(令牌)。
例如:
創(chuàng)建FormTokenServlet,用于生成Token和跳轉(zhuǎn)到token.jsp頁面:
public?class?FormTokenServlet?extends?HttpServlet?{private?static?final?long?serialVersionUID?=?-8236507185410764108L;@Overrideprotected?void?doGet(HttpServletRequest?req,?HttpServletResponse?resp)?throws?ServletException,?IOException?{doPost(req,?resp);}@Overrideprotected?void?doPost(HttpServletRequest?req,?HttpServletResponse?resp)?throws?ServletException,?IOException?{//?創(chuàng)建令牌String?token?=?TokenProccessor.getInstance().makeToken();System.out.println("在FormServlet中生成的token:"?+?token);//?在服務(wù)器使用session保存token(令牌)req.getSession().setAttribute("token",?token);//?跳轉(zhuǎn)到token.jsp頁面req.getRequestDispatcher("/token.jsp").forward(req,?resp);} }在token.jsp中使用隱藏域來存儲Token(令牌),提交Token(令牌)到服務(wù)器:
<%@?page?language="java"?contentType="text/html;?charset=UTF-8"pageEncoding="UTF-8"%> <%String?path?=?request.getContextPath();String?basePath?=?request.getScheme()?+?"://"+?request.getServerName()?+?":"?+?request.getServerPort()+?path?+?"/"; %> <html> <head> <base?href="<%=basePath%>"?/> <meta?http-equiv="Content-Type"?content="text/html;charset=UTF-8"> <title>表單提交</title> <link?href="css/bootstrap.css"?rel="stylesheet"> <script?src="js/jquery-3.2.1.js"></script> <script?src="js/bootstrap.js"></script> </head> <body><form?class="form-horizontal"?action="<%=request.getContextPath()%>/TokenServlet.html"?role="form"?method="post"><input?type="hidden"?name="token"?value="<%=session.getAttribute("token")?%>"><div?class="form-group"><label?for="firstname"?class="col-sm-1?control-label">用戶名</label><div?class="col-sm-3"><input?type="text"?class="form-control"?name="idCodeNum"placeholder="請輸入用戶名"></div></div><div?class="form-group"><div?class="col-sm-offset-1?col-sm-3"><button?type="submit"?class="btn?btn-default">提交</button><button?type="reset"?class="btn?btn-default">重置</button></div></div></form> </body> </html>TokenServlet處理表單提交:
public?class?TokenServlet?extends?HttpServlet?{private?static?final?long?serialVersionUID?=?-8236507185410764108L;@Overrideprotected?void?doGet(HttpServletRequest?req,?HttpServletResponse?resp)?throws?ServletException,?IOException?{doPost(req,?resp);}@Overrideprotected?void?doPost(HttpServletRequest?req,?HttpServletResponse?resp)?throws?ServletException,?IOException?{boolean?b?=?false;String?client_token?=?req.getParameter("token");//?如果用戶提交的表單數(shù)據(jù)中沒有token,則用戶是重復(fù)提交了表單if?(client_token?==?null)?{b?=?true;}//?取出存儲在Session中的tokenString?serverToken?=?(String)?req.getSession().getAttribute("token");//?如果當(dāng)前用戶的Session中不存在Token,則用戶是重復(fù)提交了表單if?(serverToken?==?null)?{b?=?true;}//?存儲在Session中的Token與表單提交的Token不同,則用戶是重復(fù)提交了表單if?(!client_token.equals(serverToken))?{b?=?true;}if?(b?==?true)?{System.out.println("請不要重復(fù)提交!");return;}//?移除session中的tokenreq.getSession().removeAttribute("token");System.out.println("正在處理用戶提交請求!!");} }運行結(jié)果如下:
image十、用戶禁用Cookie后的Session處理
這里存在一種情況,假如用戶瀏覽器禁用了Cookie怎么辦?比如我把Chrome的Cookie禁用,如下:
image解決方法:URL重寫
Servlet中的response提供了對URL重寫的方法:
| response.encodeURL(String url) | 用于對表單action和超鏈接的url地址進(jìn)行重寫 |
那么URL重寫是什么意思呢?其實就是人為地把JSESSIONID附在了url的后面,比如我們修改之前寫的簡易購物車,ShowBook中所有的點擊購買超鏈接都要重寫。
之前我們是這么寫的:
out.println("<tr><td>"+book.getName()+"</td><td><a?href='"+?req.getContextPath()?+"/BuyBookServlet.html?id="+book.getId()+"'>點擊購買</a></td></tr>");現(xiàn)在進(jìn)行URL重寫:
req.getSession(); String?url?=?"/MyCart/BuyBookCl?id="+book.getId(); url?=?resp.encodeURL(url); out.println("<tr><td>"+book.getName()+"</td><td><a?href='"+url+"'>點擊購買</a></td></tr>");需要注意的是,重寫之前一定要調(diào)用或者確保使用過request.getSession()這個方法。
重寫之前,訪問ShowBookServlet的源代碼是這樣的:
image重寫之后呢:
image可以看到URL重寫之后,jsessionid這個參數(shù)自動附在了url后面,由此,得以確保我們的Session在Cookie被禁用的情況下繼續(xù)正常使用。這時候我們查看購物車的地址欄如下,可以明顯看到j(luò)sessionid這個參數(shù):
image出處:
作者:Zender 原文:https://www.cnblogs.com/Zender/p/7657516.html????覺得有用就轉(zhuǎn)發(fā)分享一下吧
推薦閱讀
1.?一文讀懂HttpServletRequest
2.?一文讀懂HttpServletResponse
3.?servlet就是這么簡單
4.?tomcat基本使用,就是這么簡單
附上熱門QQ群,存放資源和歷史資料,2000容量(低門檻付費群),長按二維碼入群
總結(jié)
以上是生活随笔為你收集整理的开发中经常碰到的问题cookie和session问题,今天一并解决的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux下查找java的安装路径和ja
- 下一篇: java后端开发每天遇到的jsp,了解一