摘要:于是乎服務(wù)器向用戶瀏覽器發(fā)送了一個(gè)名為的,它的值是的值。標(biāo)記著該用戶已經(jīng)登陸了跳轉(zhuǎn)到其他頁(yè)面,告訴用戶成功登陸了。注冊(cè)多個(gè)用戶,不斷發(fā)帖子,擾亂正常發(fā)帖秩序。在處理表單的中刷新。監(jiān)聽用戶提交事件。
什么是Session
Session 是另一種記錄瀏覽器狀態(tài)的機(jī)制。不同的是Cookie保存在瀏覽器中,Session保存在服務(wù)器中。用戶使用瀏覽器訪問服務(wù)器的時(shí)候,服務(wù)器把用戶的信息以某種的形式記錄在服務(wù)器,這就是Session
如果說Cookie是檢查用戶身上的”通行證“來確認(rèn)用戶的身份,那么Session就是通過檢查服務(wù)器上的”客戶明細(xì)表“來確認(rèn)用戶的身份的。Session相當(dāng)于在服務(wù)器中建立了一份“客戶明細(xì)表”。
為什么要使用Session技術(shù)?Session比Cookie使用方便,Session可以解決Cookie解決不了的事情【Session可以存儲(chǔ)對(duì)象,Cookie只能存儲(chǔ)字符串。】。
Session APIlong getCreationTime();【獲取Session被創(chuàng)建時(shí)間】
String getId();【獲取Session的id】
long getLastAccessedTime();【返回Session最后活躍的時(shí)間】
ServletContext getServletContext();【獲取ServletContext對(duì)象】
void setMaxInactiveInterval(int var1);【設(shè)置Session超時(shí)時(shí)間】
int getMaxInactiveInterval();【獲取Session超時(shí)時(shí)間】
Object getAttribute(String var1);【獲取Session屬性】
Enumeration
void setAttribute(String var1, Object var2);【設(shè)置Session屬性】
void removeAttribute(String var1);【移除Session屬性】
void invalidate();【銷毀該Session】
boolean isNew();【該Session是否為新的】
Session作為域?qū)ο?/b>從上面的API看出,Session有著request和ServletContext類似的方法。其實(shí)Session也是一個(gè)域?qū)ο?/strong>。Session作為一種記錄瀏覽器狀態(tài)的機(jī)制,只要Session對(duì)象沒有被銷毀,Servlet之間就可以通過Session對(duì)象實(shí)現(xiàn)通訊
我們來試試吧,在Servlet4中設(shè)置Session屬性
//得到Session對(duì)象 HttpSession httpSession = request.getSession(); //設(shè)置Session屬性 httpSession.setAttribute("name", "看完博客就要點(diǎn)贊!!");
在Servlet5中獲取到Session存進(jìn)去的屬性
//獲取到從Servlet4的Session存進(jìn)去的值 HttpSession httpSession = request.getSession(); String value = (String) httpSession.getAttribute("name"); System.out.println(value);
訪問Servlet4,再訪問Servlet5
一般來講,當(dāng)我們要存進(jìn)的是用戶級(jí)別的數(shù)據(jù)就用Session,那什么是用戶級(jí)別呢?只要瀏覽器不關(guān)閉,希望數(shù)據(jù)還在,就使用Session來保存。
Session的生命周期和有效期Session在用戶第一次訪問服務(wù)器Servlet,jsp等動(dòng)態(tài)資源就會(huì)被自動(dòng)創(chuàng)建,Session對(duì)象保存在內(nèi)存里,這也就為什么上面的例子可以直接使用request對(duì)象獲取得到Session對(duì)象。
如果訪問HTML,IMAGE等靜態(tài)資源Session不會(huì)被創(chuàng)建。
Session生成后,只要用戶繼續(xù)訪問,服務(wù)器就會(huì)更新Session的最后訪問時(shí)間,無(wú)論是否對(duì)Session進(jìn)行讀寫,服務(wù)器都會(huì)認(rèn)為Session活躍了一次。
由于會(huì)有越來越多的用戶訪問服務(wù)器,因此Session也會(huì)越來越多。為了防止內(nèi)存溢出,服務(wù)器會(huì)把長(zhǎng)時(shí)間沒有活躍的Session從內(nèi)存中刪除,這個(gè)時(shí)間也就是Session的超時(shí)時(shí)間。
Session的超時(shí)時(shí)間默認(rèn)是30分鐘,有三種方式可以對(duì)Session的超時(shí)時(shí)間進(jìn)行修改
第一種方式:在tomcat/conf/web.xml文件中設(shè)置,時(shí)間值為20分鐘,所有的WEB應(yīng)用都有效
20
第二種方式:在單個(gè)的web.xml文件中設(shè)置,對(duì)單個(gè)web應(yīng)用有效,如果有沖突,以自己的web應(yīng)用為準(zhǔn)。
20
第三種方式:通過setMaxInactiveInterval()方法設(shè)置
//設(shè)置Session最長(zhǎng)超時(shí)時(shí)間為60秒,這里的單位是秒 httpSession.setMaxInactiveInterval(60); System.out.println(httpSession.getMaxInactiveInterval());
Session的有效期與Cookie的是不同的
使用Session完成簡(jiǎn)單的購(gòu)物功能我們還是以書籍為例,所以可以copy“顯示瀏覽過的商品“例子部分的代碼。
response.setContentType("text/html;charset=UTF-8"); PrintWriter printWriter = response.getWriter(); printWriter.write("網(wǎng)頁(yè)上所有的書籍:" + "
"); //拿到數(shù)據(jù)庫(kù)所有的書 LinkedHashMaplinkedHashMap = DB.getAll(); Set > entry = linkedHashMap.entrySet(); //顯示所有的書到網(wǎng)頁(yè)上 for (Map.Entry stringBookEntry : entry) { Book book = stringBookEntry.getValue(); String url = "/ouzicheng/Servlet6?id=" + book.getId(); printWriter.write(book.getName()); printWriter.write("購(gòu)買"); printWriter.write("
"); }
在購(gòu)物車頁(yè)面上,獲取到用戶想買的書籍【用戶可能不單想買一本書,于是乎,就用一個(gè)List容器裝載書籍】,有了:先遍歷Cookie,再判斷是否是第一次訪問Servlet的邏輯思路,我們就可以先獲取到Session的屬性,如果Session的屬性為null,那么就是還沒有該屬性
//得到用戶想買書籍的id String id = request.getParameter("id"); //根據(jù)書籍的id找到用戶想買的書 Book book = (Book) DB.getAll().get(id); //獲取到Session對(duì)象 HttpSession httpSession = request.getSession(); //由于用戶可能想買多本書的,所以我們用一個(gè)容器裝著書籍 List list = (List) httpSession.getAttribute("list"); if (list == null) { list = new ArrayList(); //設(shè)置Session屬性 httpSession.setAttribute("list",list); } //把書籍加入到list集合中 list.add(book);
按我們正常的邏輯思路:先創(chuàng)建一個(gè)ArrayList對(duì)象,把書加到list集合中,然后設(shè)置Session的屬性。這樣是行不通的。每次Servlet被訪問的時(shí)候都會(huì)創(chuàng)建一個(gè)ArrayList集合,書籍會(huì)被分發(fā)到不同的ArrayList中去。所以下面的代碼是不行的!
//得到用戶想買書籍的id String id = request.getParameter("id"); //根據(jù)書籍的id找到用戶想買的書 Book book = (Book) DB.getAll().get(id); //獲取到Session對(duì)象 HttpSession httpSession = request.getSession(); //創(chuàng)建List集合 List list = new ArrayList(); list.add(book); httpSession.setAttribute("list", list);
既然用戶已經(jīng)購(gòu)買了書籍,那么也應(yīng)該給提供頁(yè)面顯示用戶購(gòu)買過哪些書籍
//得到用戶想買書籍的id String id = request.getParameter("id"); //根據(jù)書籍的id找到用戶想買的書 Book book = (Book) DB.getAll().get(id); //獲取到Session對(duì)象 HttpSession httpSession = request.getSession(); //由于用戶可能想買多本書的,所以我們用一個(gè)容器裝著書籍 List list = (List) httpSession.getAttribute("list"); if (list == null) { list = new ArrayList(); //設(shè)置Session屬性 httpSession.setAttribute("list",list); } //把書籍加入到list集合中 list.add(book); String url = "/ouzicheng/Servlet7"; response.sendRedirect(url);
列出用戶購(gòu)買過的書籍
//要得到用戶購(gòu)買過哪些書籍,得到Session的屬性遍歷即可 HttpSession httpSession = request.getSession(); Listlist = (List) httpSession.getAttribute("list"); if (list == null || list.size() == 0) { printWriter.write("對(duì)不起,你還沒有買過任何商品"); } else { printWriter.write("您購(gòu)買過以下商品:"); printWriter.write("
"); for (Book book : list) { printWriter.write(book.getName()); printWriter.write("
"); } }
效果如下
Session的實(shí)現(xiàn)原理用現(xiàn)象說明問題,我在Servlet4中的代碼設(shè)置了Session的屬性
//得到Session對(duì)象 HttpSession httpSession = request.getSession(); //設(shè)置Session屬性 httpSession.setAttribute("name", "看完博客就要點(diǎn)贊!!");
接著在Servlet7把Session的屬性取出來
String value = (String) request.getSession().getAttribute("name"); printWriter.write(value);
自然地,我們能取到在Servlet4中Session設(shè)置的屬性
接著,我在瀏覽器中新建一個(gè)會(huì)話,再次訪問Servlet7
發(fā)現(xiàn)報(bào)了空指針異常的錯(cuò)誤
現(xiàn)在問題來了:服務(wù)器是如何實(shí)現(xiàn)一個(gè)session為一個(gè)用戶瀏覽器服務(wù)的?換個(gè)說法:為什么服務(wù)器能夠?yàn)椴煌挠脩魹g覽器提供不同session?
HTTP協(xié)議是無(wú)狀態(tài)的,Session不能依據(jù)HTTP連接來判斷是否為同一個(gè)用戶。于是乎:服務(wù)器向用戶瀏覽器發(fā)送了一個(gè)名為JESSIONID的Cookie,它的值是Session的id值。其實(shí)Session依據(jù)Cookie來識(shí)別是否是同一個(gè)用戶。
簡(jiǎn)單來說:Session 之所以可以識(shí)別不同的用戶,依靠的就是Cookie
該Cookie是服務(wù)器自動(dòng)頒發(fā)給瀏覽器的,不用我們手工創(chuàng)建的。該Cookie的maxAge值默認(rèn)是-1,也就是說僅當(dāng)前瀏覽器使用,不將該Cookie存在硬盤中
我們來捋一捋思路流程:當(dāng)我們?cè)L問Servlet4的時(shí)候,服務(wù)器就會(huì)創(chuàng)建一個(gè)Session對(duì)象,執(zhí)行我們的程序代碼,并自動(dòng)頒發(fā)個(gè)Cookie給用戶瀏覽器
當(dāng)我們用同一個(gè)瀏覽器訪問Servlet7的時(shí)候,瀏覽器會(huì)把Cookie的值通過http協(xié)議帶過去給服務(wù)器,服務(wù)器就知道用哪一Session。
而當(dāng)我們使用新會(huì)話的瀏覽器訪問Servlet7的時(shí)候,該新瀏覽器并沒有Cookie,服務(wù)器無(wú)法辨認(rèn)使用哪一個(gè)Session,所以就獲取不到值
瀏覽器禁用了Cookie,Session還能用嗎?上面說了Session是依靠Cookie來識(shí)別用戶瀏覽器的。如果我的用戶瀏覽器禁用了Cookie了呢?絕大多數(shù)的手機(jī)瀏覽器都不支持Cookie,那我的Session怎么辦?
好的,我們來看看情況是怎么樣的。用戶瀏覽器訪問Servlet4的時(shí)候,服務(wù)器向用戶瀏覽器頒發(fā)了一個(gè)Cookie
但是呢,當(dāng)用戶瀏覽器訪問Servlet7的時(shí)候,由于我們禁用了Cookie,所以用戶瀏覽器并沒有把Cookie帶過去給服務(wù)器。
一看,Session好像不能用了。但是Java Web提供了解決方法:URL地址重寫
HttpServletResponse類提供了兩個(gè)URL地址重寫的方法:
encodeURL(String url)
encodeRedirectURL(String url)
需要值得注意的是:這兩個(gè)方法會(huì)自動(dòng)判斷該瀏覽器是否支持Cookie,如果支持Cookie,重寫后的URL地址就不會(huì)帶有jsessionid了【當(dāng)然了,即使瀏覽器支持Cookie,第一次輸出URL地址的時(shí)候還是會(huì)出現(xiàn)jsessionid(因?yàn)闆]有任何Cookie可帶)】
下面我們就以上面“購(gòu)物”的例子來做試驗(yàn)吧!首先我們來看看禁用掉Cookie對(duì)原來的小例子有什么影響。
訪問Servlet1,隨便點(diǎn)擊一本書籍購(gòu)買
無(wú)論點(diǎn)擊多少次,都會(huì)直接提示我們有買過任何商品
原因也非常簡(jiǎn)單,沒有Cookie傳遞給服務(wù)器,服務(wù)器每次創(chuàng)建的時(shí)候都是新的Session,導(dǎo)致最后獲取到的List集合一定是空的。
不同Servlet獲取到的Session的id號(hào)都是不同的。
下面我們就對(duì)URL進(jìn)行重寫,看看能不能恢復(fù)沒有禁掉Cookie之前的效果。
原則:把Session的屬性帶過去【傳遞給】另外一個(gè)Servlet,都要URL地址重寫
在跳轉(zhuǎn)到顯示購(gòu)買過商品的Servlet的時(shí)候,URL地址重寫。
String url = "/ouzicheng/Servlet7"; response.sendRedirect(response.encodeURL(url));
再次訪問Servlet1,當(dāng)我點(diǎn)擊javaweb的時(shí)候,已經(jīng)能夠成功出現(xiàn)我買過的商品了。并且Session的id通過URL地址重寫,使用的是同一個(gè)Session
URL地址重寫的原理:將Session的id信息重寫到URL地址中。服務(wù)器解析重寫后URL,獲取Session的id。這樣一來,即使瀏覽器禁用掉了Cookie,但Session的id通過服務(wù)器端傳遞,還是可以使用Session來記錄用戶的狀態(tài)。
Session禁用CookieJava Web規(guī)范支持通過配置禁用Cookie
禁用自己項(xiàng)目的Cookie
在META-INF文件夾下的context.xml文件中修改(沒有則創(chuàng)建)
![](http://i.imgur.com/Szu5o1m.png) ```xml```
禁用全部web應(yīng)用的Cookie
在conf/context.xml中修改
![](http://i.imgur.com/dFrvztb.png)
注意:該配置只是讓服務(wù)器不能自動(dòng)維護(hù)名為jsessionid的Cookie,并不能阻止Cookie的讀寫。
Session案例 使用Session完成用戶簡(jiǎn)單登陸先創(chuàng)建User類
private String username = null; private String password = null; public User() { } public User(String username, String password) { this.username = username; this.password = password; } ....各種set、get方法
使用簡(jiǎn)單的集合模擬一個(gè)數(shù)據(jù)庫(kù)
private static Listlist = new ArrayList<>(); //裝載些數(shù)據(jù)進(jìn)數(shù)據(jù)庫(kù) static { list.add(new User("aaa","111")); list.add(new User("bbb","222")); list.add(new User("ccc","333")); } //通過用戶名和密碼查找用戶 public static User find(String username, String password) { for (User user : list) { if (user.getUsername().equals(username) && user.getPassword().equals(password)) { return user; } } return null; }
表單提交的工作我就在jsp寫了,如果在Servlet寫太麻煩了!
獲取到表單提交的數(shù)據(jù),查找數(shù)據(jù)庫(kù)是否有相對(duì)應(yīng)的用戶名和密碼。如果沒有就提示用戶名或密碼出錯(cuò)了,如果有就跳轉(zhuǎn)到另外一個(gè)頁(yè)面
String username = request.getParameter("username"); String password = request.getParameter("password"); User user = UserDB.find(username, password); //如果找不到,就是用戶名或密碼出錯(cuò)了。 if (user == null) { response.getWriter().write("you can"t login"); return; } //標(biāo)記著該用戶已經(jīng)登陸了! HttpSession httpSession = request.getSession(); httpSession.setAttribute("user", user); //跳轉(zhuǎn)到其他頁(yè)面,告訴用戶成功登陸了。 response.sendRedirect(response.encodeURL("index.jsp"));
我們來試試下數(shù)據(jù)庫(kù)沒有的用戶名和密碼,提示我不能登陸。
試試數(shù)據(jù)庫(kù)存在的用戶名和密碼
利用Session防止表單重復(fù)提交
重復(fù)提交的危害:
在投票的網(wǎng)頁(yè)上不停地提交,實(shí)現(xiàn)了刷票的效果。
注冊(cè)多個(gè)用戶,不斷發(fā)帖子,擾亂正常發(fā)帖秩序。
首先我們來看一下常見的重復(fù)提交。
在處理表單的Servlet中刷新。
后退再提交
網(wǎng)絡(luò)延遲,多次點(diǎn)擊提交按鈕
下面的gif是后退再提交,在處理提交請(qǐng)求的Servlet中刷新
下面的gif是網(wǎng)絡(luò)延遲,多次點(diǎn)擊提交按鈕
對(duì)于網(wǎng)絡(luò)延遲造成的多次提交數(shù)據(jù)給服務(wù)器,其實(shí)是客戶端的問題。于是,我們可以使用javaScript來防止這種情況
要做的事情也非常簡(jiǎn)單:當(dāng)用戶第一次點(diǎn)擊提交按鈕時(shí),把數(shù)據(jù)提交給服務(wù)器。當(dāng)用戶再次點(diǎn)擊提交按鈕時(shí),就不把數(shù)據(jù)提交給服務(wù)器了。
監(jiān)聽用戶提交事件。只能讓用戶提交一次表單!
<%@ page contentType="text/html;charset=UTF-8" language="java" %>表單提交
好的,我們來試一下是不是真的可以解決網(wǎng)絡(luò)延遲所造成的多次提交表單數(shù)據(jù),注意鼠標(biāo),我已經(jīng)點(diǎn)擊過很多次的了!
由于網(wǎng)絡(luò)延遲造成的多次提交數(shù)據(jù)給服務(wù)器,我們還可以使用javaScript代碼這樣解決:當(dāng)我點(diǎn)擊過一次提交按鈕時(shí),我就把提交的按鈕隱藏起來。不能讓用戶點(diǎn)擊了!
想要讓按鈕隱藏起來,也很簡(jiǎn)單。只要獲取到按鈕的節(jié)點(diǎn),就可以控制按鈕的隱藏或顯示了!
我們?cè)賮砜匆幌滦Ч?/p>
在處理表單的Servlet中刷新和后退再提交這兩種方式不能只靠客戶端來限制了。也就是說javaScript代碼無(wú)法阻止這兩種情況的發(fā)生。
于是乎,我們就想得用其他辦法來阻止表單數(shù)據(jù)重復(fù)提交了。我們現(xiàn)在學(xué)了Session,Session可以用來標(biāo)識(shí)一個(gè)用戶是否登陸了。Session的原理也說了:不同的用戶瀏覽器會(huì)擁有不同的Session。而request和ServletContext為什么就不行呢?request的域?qū)ο笾荒苁且淮蝖ttp請(qǐng)求,提交表單數(shù)據(jù)的時(shí)候request域?qū)ο蟮臄?shù)據(jù)取不出來。ServletContext代表整個(gè)web應(yīng)用,如果有幾個(gè)用戶瀏覽器同時(shí)訪問,ServletContext域?qū)ο蟮臄?shù)據(jù)會(huì)被多次覆蓋掉,也就是說域?qū)ο蟮臄?shù)據(jù)就毫無(wú)意義了。
可能到這里,我們會(huì)想到:在提交數(shù)據(jù)的時(shí)候,存進(jìn)Session域?qū)ο蟮臄?shù)據(jù),在處理提交數(shù)據(jù)的Servlet中判斷Session域?qū)ο髷?shù)據(jù)????。究竟判斷Session什么?判斷Session域?qū)ο蟮臄?shù)據(jù)不為null?沒用呀,既然已經(jīng)提交過來了,那肯定不為null。
此時(shí),我們就想到了,在表單中還有一個(gè)隱藏域,可以通過隱藏域把數(shù)據(jù)交給服務(wù)器。
判斷Session域?qū)ο蟮臄?shù)據(jù)和jsp隱藏域提交的數(shù)據(jù)是否對(duì)應(yīng)。
判斷隱藏域的數(shù)據(jù)是否為空【如果為空,就是直接訪問表單處理頁(yè)面的Servlet】
判斷Session的數(shù)據(jù)是否為空【servlet判斷完是否重復(fù)提交,最好能立馬移除Session的數(shù)據(jù),不然還沒有移除的時(shí)候,客戶端那邊兒的請(qǐng)求又來了,就又能匹配了,產(chǎn)生了重復(fù)提交。如果Session域?qū)ο髷?shù)據(jù)為空,證明已經(jīng)提交過數(shù)據(jù)了!】
我們向Session域?qū)ο蟮拇嫒霐?shù)據(jù)究竟是什么呢?簡(jiǎn)單的一個(gè)數(shù)字?好像也行啊。因?yàn)?strong>只要Session域?qū)ο蟮臄?shù)據(jù)和jsp隱藏域帶過去的數(shù)據(jù)對(duì)得上號(hào)就行了呀,反正在Servlet上判斷完是否重復(fù)提交,會(huì)立馬把Session的數(shù)據(jù)移除掉的。更專業(yè)的做法是:向Session域?qū)ο蟠嫒氲臄?shù)據(jù)是一個(gè)隨機(jī)數(shù)【Token--令牌】。
生成一個(gè)獨(dú)一無(wú)二的隨機(jī)數(shù)
/* * 產(chǎn)生隨機(jī)數(shù)就應(yīng)該用一個(gè)對(duì)象來生成,這樣可以避免隨機(jī)數(shù)的重復(fù)。 * 所以設(shè)計(jì)成單例 * */ public class TokenProcessor { private TokenProcessor() { } private final static TokenProcessor TOKEN_PROCESSOR = new TokenProcessor(); public static TokenProcessor getInstance() { return TOKEN_PROCESSOR; } public static String makeToken() { //這個(gè)隨機(jī)生成出來的Token的長(zhǎng)度是不確定的 String token = String.valueOf(System.currentTimeMillis() + new Random().nextInt(99999999)); try { //我們想要隨機(jī)數(shù)的長(zhǎng)度一致,就要獲取到數(shù)據(jù)指紋 MessageDigest messageDigest = MessageDigest.getInstance("md5"); byte[] md5 = messageDigest.digest(token.getBytes()); //如果我們直接 return new String(md5)出去,得到的隨機(jī)數(shù)會(huì)亂碼。 //因?yàn)殡S機(jī)數(shù)是任意的01010101010,在轉(zhuǎn)換成字符串的時(shí)候,會(huì)查gb2312的碼表,gb2312碼表不一定支持該二進(jìn)制數(shù)據(jù),得到的就是亂碼 //于是乎經(jīng)過base64編碼成了明文的數(shù)據(jù) BASE64Encoder base64Encoder = new BASE64Encoder(); return base64Encoder.encode(md5); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } return null; } }
創(chuàng)建Token隨機(jī)數(shù),并跳轉(zhuǎn)到j(luò)sp頁(yè)面
//生出隨機(jī)數(shù) TokenProcessor tokenProcessor = TokenProcessor.getInstance(); String token = tokenProcessor.makeToken(); //將隨機(jī)數(shù)存進(jìn)Session中 request.getSession().setAttribute("token", token); //跳轉(zhuǎn)到顯示頁(yè)面 request.getRequestDispatcher("/login.jsp").forward(request, response);
jsp隱藏域獲取到Session的值
在處理表單提交頁(yè)面中判斷:jsp隱藏域是否有值帶過來,Session中的值是否為空,Session中的值和jsp隱藏域帶過來的值是否相等
String serverValue = (String) request.getSession().getAttribute("token"); String clientValue = request.getParameter("token"); if (serverValue != null && clientValue != null && serverValue.equals(clientValue)) { System.out.println("處理請(qǐng)求"); //清除Session域?qū)ο髷?shù)據(jù) request.getSession().removeAttribute("token"); }else { System.out.println("請(qǐng)不要重復(fù)提交數(shù)據(jù)!"); }
下面我們?cè)賮砜匆幌?已經(jīng)可以解決表單重復(fù)提交的問題了!
實(shí)現(xiàn)原理是非常簡(jiǎn)單的:
在session域中存儲(chǔ)一個(gè)token
然后前臺(tái)頁(yè)面的隱藏域獲取得到這個(gè)token
在第一次訪問的時(shí)候,我們就判斷seesion有沒有值,如果有就比對(duì)。對(duì)比正確后我們就處理請(qǐng)求,接著就把session存儲(chǔ)的數(shù)據(jù)給刪除了
等到再次訪問的時(shí)候,我們session就沒有值了,就不受理前臺(tái)的請(qǐng)求了!
一次性校驗(yàn)碼一次性校驗(yàn)碼其實(shí)就是為了防止暴力猜測(cè)密碼
在講response對(duì)象的時(shí)候,我們使用response對(duì)象輸出過驗(yàn)證碼,但是沒有去驗(yàn)證!
驗(yàn)證的原理也非常簡(jiǎn)單:生成驗(yàn)證碼后,把驗(yàn)證碼的數(shù)據(jù)存進(jìn)Session域?qū)ο笾校袛嘤脩糨斎腧?yàn)證碼是否和Session域?qū)ο蟮臄?shù)據(jù)一致。
生成驗(yàn)證碼圖片,并將驗(yàn)證碼存進(jìn)Session域中
//在內(nèi)存中生成圖片 BufferedImage bufferedImage = new BufferedImage(80, 20, BufferedImage.TYPE_INT_RGB); //獲取到這張圖片 Graphics2D graphics2D = (Graphics2D) bufferedImage.getGraphics(); //設(shè)置背景色為白色 graphics2D.setColor(Color.white); graphics2D.fillRect(0, 0, 80, 20); //設(shè)置圖片的字體和顏色 graphics2D.setFont(new Font(null, Font.BOLD, 20)); graphics2D.setColor(Color.BLUE); //生成隨機(jī)數(shù) String randomNum = makeNum(); //往這張圖片上寫數(shù)據(jù),橫坐標(biāo)是0,縱坐標(biāo)是20 graphics2D.drawString(randomNum, 0, 20); //將隨機(jī)數(shù)存進(jìn)Session域中 request.getSession().setAttribute("randomNum", randomNum); //控制瀏覽器不緩存該圖片 response.setHeader("Expires", "-1"); response.setHeader("Cache-Control", "no-cache"); response.setHeader("Pragma", "no-cache"); //通知瀏覽器以圖片的方式打開 response.setHeader("Content-type", "image/jpeg"); //把圖片寫給瀏覽器 ImageIO.write(bufferedImage, "jpg", response.getOutputStream());
生成隨機(jī)數(shù)的方法:
private String makeNum() { Random random = new Random(); //生成0-6位的隨機(jī)數(shù) int num = random.nextInt(999999); //驗(yàn)證碼的數(shù)位全都要6位數(shù),于是將該隨機(jī)數(shù)轉(zhuǎn)換成字符串,不夠位數(shù)就添加 String randomNum = String.valueOf(num); //使用StringBuffer來拼湊字符串 StringBuffer stringBuffer = new StringBuffer(); for (int i = 0; i < 6 - randomNum.length(); i++) { stringBuffer.append("0"); } return stringBuffer.append(randomNum).toString(); }
jsp顯示頁(yè)面
處理提交表單數(shù)據(jù)的Servlet,判斷用戶帶過來驗(yàn)證碼的數(shù)據(jù)是否和Session的數(shù)據(jù)相同。
//獲取用戶輸入驗(yàn)證碼的數(shù)據(jù) String client_randomNum = request.getParameter("randomNum"); //獲取Session中的數(shù)據(jù) String session_randomNum = (String) request.getSession().getAttribute("randomNum"); //判斷他倆數(shù)據(jù)是否相等,用戶是否有輸入驗(yàn)證碼,Session中是否為空 if (client_randomNum == null || session_randomNum == null || !client_randomNum.equals(session_randomNum)) { System.out.println("驗(yàn)證碼錯(cuò)誤了!!!"); return ; } //下面就是驗(yàn)證用戶名和密碼...................
顯示頁(yè)面是這樣子的
我們來看一下效果!
對(duì)于校驗(yàn)碼實(shí)現(xiàn)思路是這樣子的:
使用awt語(yǔ)法來描寫一張驗(yàn)證碼,生成隨機(jī)數(shù)保存在seesion域中,我們讓驗(yàn)證碼不能緩存起來【做到驗(yàn)證碼都不一樣】
頁(yè)面直接訪問Servlet來獲取我們的驗(yàn)證碼,于是我們驗(yàn)證碼的值就會(huì)改變【同時(shí)session的值也會(huì)被改變】
當(dāng)用戶驗(yàn)證的時(shí)候,就是session內(nèi)的值的驗(yàn)證了。
Session和Cookie的區(qū)別
從存儲(chǔ)方式上比較
Cookie只能存儲(chǔ)字符串,如果要存儲(chǔ)非ASCII字符串還要對(duì)其編碼。
Session可以存儲(chǔ)任何類型的數(shù)據(jù),可以把Session看成是一個(gè)容器
從隱私安全上比較
Cookie存儲(chǔ)在瀏覽器中,對(duì)客戶端是可見的。信息容易泄露出去。如果使用Cookie,最好將Cookie加密
Session存儲(chǔ)在服務(wù)器上,對(duì)客戶端是透明的。不存在敏感信息泄露問題。
從有效期上比較
Cookie保存在硬盤中,只需要設(shè)置maxAge屬性為比較大的正整數(shù),即使關(guān)閉瀏覽器,Cookie還是存在的
Session的保存在服務(wù)器中,設(shè)置maxInactiveInterval屬性值來確定Session的有效期。并且Session依賴于名為JSESSIONID的Cookie,該Cookie默認(rèn)的maxAge屬性為-1。如果關(guān)閉了瀏覽器,該Session雖然沒有從服務(wù)器中消亡,但也就失效了。
從對(duì)服務(wù)器的負(fù)擔(dān)比較
Session是保存在服務(wù)器的,每個(gè)用戶都會(huì)產(chǎn)生一個(gè)Session,如果是并發(fā)訪問的用戶非常多,是不能使用Session的,Session會(huì)消耗大量的內(nèi)存。
Cookie是保存在客戶端的。不占用服務(wù)器的資源。像baidu、Sina這樣的大型網(wǎng)站,一般都是使用Cookie來進(jìn)行會(huì)話跟蹤。
從瀏覽器的支持上比較
如果瀏覽器禁用了Cookie,那么Cookie是無(wú)用的了!
如果瀏覽器禁用了Cookie,Session可以通過URL地址重寫來進(jìn)行會(huì)話跟蹤。
從跨域名上比較
Cookie可以設(shè)置domain屬性來實(shí)現(xiàn)跨域名
Session只在當(dāng)前的域名內(nèi)有效,不可夸域名
Cookie和Session共同使用如果僅僅使用Cookie或僅僅使用Session可能達(dá)不到理想的效果。這時(shí)應(yīng)該嘗試一下同時(shí)使用Session和Cookie
那么,什么時(shí)候才需要同時(shí)使用Cookie和Session呢?
在上一篇博客中,我們使用了Session來進(jìn)行簡(jiǎn)單的購(gòu)物,功能也的確實(shí)現(xiàn)了。現(xiàn)在有一個(gè)問題:我在購(gòu)物的途中,不小心關(guān)閉了瀏覽器。當(dāng)我再返回進(jìn)去瀏覽器的時(shí)候,發(fā)現(xiàn)我購(gòu)買過的商品記錄都沒了!!為什么會(huì)沒了呢?原因也非常簡(jiǎn)單:服務(wù)器為Session自動(dòng)維護(hù)的Cookie的maxAge屬性默認(rèn)是-1的,當(dāng)瀏覽器關(guān)閉掉了,該Cookie就自動(dòng)消亡了。當(dāng)用戶再次訪問的時(shí)候,已經(jīng)不是原來的Cookie了。
我們現(xiàn)在想的是:即使我不小心關(guān)閉了瀏覽器了,我重新進(jìn)去網(wǎng)站,我還能找到我的購(gòu)買記錄。
要實(shí)現(xiàn)該功能也十分簡(jiǎn)單,問題其實(shí)就在:服務(wù)器為Session自動(dòng)維護(hù)的Cookie的maxAge屬性是-1,Cookie沒有保存在硬盤中。我現(xiàn)在要做的就是:把Cookie保存在硬盤中,即使我關(guān)閉了瀏覽器,瀏覽器再次訪問頁(yè)面的時(shí)候,可以帶上Cookie,從而服務(wù)器識(shí)別出Session。
第一種方式:只需要在處理購(gòu)買頁(yè)面上創(chuàng)建Cookie,Cookie的值是Session的id返回給瀏覽器即可
Cookie cookie = new Cookie("JSESSIONID",session.getId()); cookie.setMaxAge(30*60); cookie.setPath("/ouzicheng/"); response.addCookie(cookie);
第二種方式: 在server.xml文件中配置,將每個(gè)用戶的Session在服務(wù)器關(guān)閉的時(shí)候序列化到硬盤或數(shù)據(jù)庫(kù)上保存。但此方法不常用,知道即可!
下面看一下效果
如果文章有錯(cuò)的地方歡迎指正,大家互相交流。習(xí)慣在微信看技術(shù)文章的同學(xué),可以關(guān)注微信公眾號(hào):Java3y
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/68432.html
摘要:于是乎服務(wù)器向用戶瀏覽器發(fā)送了一個(gè)名為的,它的值是的值。標(biāo)記著該用戶已經(jīng)登陸了跳轉(zhuǎn)到其他頁(yè)面,告訴用戶成功登陸了。注冊(cè)多個(gè)用戶,不斷發(fā)帖子,擾亂正常發(fā)帖秩序。在處理表單的中刷新。監(jiān)聽用戶提交事件。 什么是Session Session 是另一種記錄瀏覽器狀態(tài)的機(jī)制。不同的是Cookie保存在瀏覽器中,Session保存在服務(wù)器中。用戶使用瀏覽器訪問服務(wù)器的時(shí)候,服務(wù)器把用戶的信息以某種...
摘要:前言由于寫的文章已經(jīng)是有點(diǎn)多了,為了自己和大家的檢索方便,于是我就做了這么一個(gè)博客導(dǎo)航。 前言 由于寫的文章已經(jīng)是有點(diǎn)多了,為了自己和大家的檢索方便,于是我就做了這么一個(gè)博客導(dǎo)航。 由于更新比較頻繁,因此隔一段時(shí)間才會(huì)更新目錄導(dǎo)航哦~想要獲取最新原創(chuàng)的技術(shù)文章歡迎關(guān)注我的公眾號(hào):Java3y Java3y文章目錄導(dǎo)航 Java基礎(chǔ) 泛型就這么簡(jiǎn)單 注解就這么簡(jiǎn)單 Druid數(shù)據(jù)庫(kù)連接池...
摘要:系列文章請(qǐng)查看超詳細(xì)的面試題總結(jié)一之基礎(chǔ)知識(shí)篇超詳細(xì)的面試題總結(jié)二之基礎(chǔ)知識(shí)篇超詳細(xì)的面試題總結(jié)三之集合篇常見問題下面的都是自己之前在學(xué)習(xí)的時(shí)候總結(jié)的,對(duì)于鞏固的基礎(chǔ)知識(shí)應(yīng)該有很大幫助。注意多線程的并發(fā)的讀寫類屬性會(huì)導(dǎo)致數(shù)據(jù)不同步。 系列文章請(qǐng)查看: 超詳細(xì)的Java面試題總結(jié)(一)之Java基礎(chǔ)知識(shí)篇 超詳細(xì)的Java面試題總結(jié)(二)之Java基礎(chǔ)知識(shí)篇 超詳細(xì)的Java面試題總結(jié)(...
摘要:協(xié)議是無(wú)狀態(tài)的,一旦數(shù)據(jù)交換完畢,客戶端與服務(wù)器端的連接就會(huì)關(guān)閉,再次交換建立新的連接,也就是說,服務(wù)器無(wú)法跟蹤會(huì)話。而和就是用與解決這種問題。值得一提的是是建立在的基礎(chǔ)上創(chuàng)建的。注意在的文件中,設(shè)置了的生命周期最長(zhǎng)為分鐘。 在將cookie 和 session 之前需要先理解什么是會(huì)話會(huì)話: 用戶打開一個(gè)瀏覽器,點(diǎn)擊多個(gè)超鏈接,訪問多個(gè)web資源,然后關(guān)閉瀏覽器,整個(gè)過程稱為一個(gè)...
閱讀 3780·2021-08-30 09:47
閱讀 3710·2019-08-30 15:56
閱讀 681·2019-08-30 14:18
閱讀 703·2019-08-29 16:17
閱讀 2070·2019-08-29 11:07
閱讀 648·2019-08-26 13:53
閱讀 3452·2019-08-26 10:26
閱讀 2499·2019-08-23 18:30