前言:
application
Application用於保存所有用戶的公共的數據信息,在這只是提一下,不過多解釋。
下面我主要解說cookie和session,不過在解說之前有必要讓大家先了解一下HTTP協議和會話跟蹤
HTTP協議
協議是指計算機通信網絡中兩台計算機之間進行通信所必須共同遵守的規定或規則
HTTP協議工作於客戶端-服務端架構上。瀏覽器作為HTTP客戶端通過URL向HTTP服務端即WEB服務器發送所有請求。再通俗點講,Http協議基本是有一個request/response模型也就是請求/響應模型的,通俗講也就是“一問一答”的模式,瀏覽器向服務器發起request請求,這就是“問”;服務器收到請求后,返回response響應,這就是“答”。
需要強調的是HTTP是無連接並且是無狀態的
無連接:無連接的含義是限制每次連接只處理一個請求。服務器處理完客戶的請求,並收到客戶的應答后,即斷開連接。采用這種方式可以節省傳輸時間。
無狀態:無狀態是指協議對於事務處理沒有記憶能力。缺少狀態意味着如果后續處理需要前面的信息,則它必須重傳,這樣可能導致每次連接傳送的數據量增大。
HTTP協議是無狀態的協議。一旦數據交換完畢,客戶端與服務器端的連接就會關閉,再次交換數據需要建立新的連接。這就意味着服務器無法從連接上跟蹤會話。
會話跟蹤
在計算機術語中,會話是指一個終端用戶與交互系統進行通訊的過程,比如從輸入賬戶密碼進入操作系統到退出操作系統就是一個會話過程。會話較多用於網絡上,TCP的三次握手就創建了一個會話,TCP關閉連接就是關閉會話。
可能這些官方的話有些新手朋友看了不理解,沒關系,我再通俗點,假如你就是用戶,你的電腦就是服務器,此時你想訪問我的博客園,我的博客園網址是https://www.cnblogs.com/zyx110/,然后你把我的博客園地址復制粘貼到了瀏覽器頁面的地址欄里,敲一下回車鍵,從你復制粘貼網址到敲擊回車鍵這個過程就是客戶端向服務器發送一個請求(request),等你敲擊過回車鍵后,你發現你的瀏覽器頁面跳轉到了泰斗賢若如的博客園,這個過程就是服務器給客戶端的一個響應(response),再聯想上面說到的HTTP的無連接和無狀態,客戶端和服務器之間只有一次聯系,而且“說了下句忘了上句”,還有就是這只是客戶端和服務器端間的聯系,瀏覽器的請求(request)與請求之間是沒有關系的。但是我們想要開發Web應用,就應該讓這些請求之間有關系,這就需要我們在多個request請求之間創建一些聯系,這就是會話(session),會話簡單點講就是“要想順利交談,需要說了下句想起來上句”,多次HTTP連接間維護用戶與同一用戶發出的不同請求之間關聯的情況稱為維護一個會話,所以這些建立聯系的request請求是屬於某個(會話)session的。
再舉個例子吧,可能有些朋友看了上面這些還有點猶豫,不要緊,慢慢理解,理論上,一個用戶的所有請求操作都應該屬於同一個會話,而另一個用戶的所有請求操作則應該屬於另一個會話,二者不能混淆。例如,你是用戶,你在淘寶購買的任何商品都應該放在你的購物車內,不管你是什么時間購買的,這都是屬於同一個會話的,不能放入用戶B或用戶C的購物車內,這不屬於同一個會話。而Web應用程序是使用HTTP協議傳輸數據的。HTTP協議是無連接無狀態的協議。一旦數據交換完畢,客戶端與服務器端的連接就會關閉,再次交換數據需要建立新的連接。這就意味着服務器無法從連接上跟蹤會話。即用戶A購買了一件商品放入購物車內,當再次購買商品時服務器已經無法判斷該購買行為是屬於用戶A的會話還是用戶B的會話了。要跟蹤該會話,必須引入一種機制。
Cookie就是這樣的一種機制。它可以彌補HTTP協議無狀態的不足。在Session出現之前,基本上所有的網站都采用Cookie來跟蹤會話。
會話(Session)跟蹤是Web程序中常用的技術,用來跟蹤用戶的整個會話。常用的會話跟蹤技術是Cookie與Session。Cookie通過在客戶端記錄信息確定用戶身份,Session通過在服務器端記錄信息確定用戶身份。
cookie
由於HTTP是一種無狀態的協議,服務器單從網絡連接上無從知道客戶身份。用戶A購買了一件商品放入購物車內,當再次購買商品時服務器已經無法判斷該購買行為是屬於用戶A的會話還是用戶B的會話了。怎么辦呢?就給客戶端們頒發一個通行證吧,每人一個,無論誰訪問都必須攜帶自己通行證。這樣服務器就能從通行證上確認客戶身份了。這就是Cookie 的工作原理。
Cookie實際上是一小段的文本信息。客戶端請求服務器,如果服務器需要記錄該用戶狀態,就使用response向客戶端瀏覽器頒發一個Cookie(通行證)。客戶端會把Cookie保存起來。
當瀏覽器再請求該網站時,瀏覽器把請求的網址連同該Cookie一同提交給服務器。服務器檢查該Cookie,以此來辨認用戶狀態。服務器還可以根據需要修改Cookie的內容。
我們訪問瀏覽器的時候,瀏覽器會發送一個HTTP請求到服務器端;
服務器會發送一個HTTP響應到客戶端,其中包括Sst-Cookie,意思就是瀏覽器建立一個cookie保存服務器指定的內容,比如用戶信息和用戶操作信息;
瀏覽器保存好信息之后,下次我們再次訪問網站的時候,瀏覽器再發送HTTP請求到服務器端時都會攜帶之前保存的cookie;
服務器端會從收到的cookie中識別用戶身份,就能讓頁面為你提供專門屬於你的內容了。
比如我們從網站的登陸界面中看到有記住用戶名這個選項,你勾選了它以后,登錄成功,瀏覽器就會把你的信息放在cookie里,下次再訪問這個網站的時候,服務器就能根據收到的cookie識別出是你,幫你自動登陸,顯示專屬於你的內容。
session
Session是另一種記錄客戶狀態的機制,不同的是Cookie保存在客戶端瀏覽器中,而Session保存在服務器上。客戶端瀏覽器訪問服務器的時候,服務器把客戶端信息以某種形式記錄
在服務器上。這就是Session。客戶端瀏覽器再次訪問時只需要從該Session中查找該客戶的狀態就可以了。
每個用戶訪問服務器都會建立一個session,那服務器是怎么標識用戶的唯一身份呢?事實上,用戶與服務器建立連接的同時,服務器會自動為其分配一個SessionId。
思考兩個問題:
-
什么東西可以讓你每次請求都把SessionId自動帶到服務器呢?
顯然就是cookie了,如果你想為用戶建立一次會話,可以在用戶授權成功時給他一個唯一的cookie。當一個
用戶提交了表單時,瀏覽器會將用戶的SessionId自動附加在HTTP頭信息中,(這是瀏覽器的自動功能,用戶不會察覺到),當服務器處理完這個表單后,將結果返回給SessionId
所對應的用戶。試想,如果沒有 SessionId,當有兩個用戶同時進行注冊時,服務器怎樣才能知道到底是哪個用戶提交了哪個表單呢。
web開發發展至今,cookie和session的使用已經出現了一些非常成熟的方案。在如今的市場或者企業里,一般有兩種存儲方式:
-
存儲在服務器端:通過cookie存儲一個session_id,然后具體的數據則是保存在session中。如果用戶已經登錄,則服務器會在cookie中保存一個session_id,下次再次請求的時候,會把該session_id攜帶上來,服務器根據session_id在session庫中獲取用戶的session數據。就能知道該用戶到底是誰,以及之前保存的一些狀態信息。這種專業術語叫做server side session。
-
將session數據加密,然后存儲在cookie中。這種專業術語叫做client side session。flask采用的就是這種方式,但是也可以替換成其他形式。
-
如何儲存需要的信息?
服務器通過SessionId作為key,讀寫到對應的value,這就達到了保持會話信息的目的。
session的創建:
當程序需要為某個客戶端的請求創建一個session時,服務器首先檢查這個客戶端的請求里是否已包含了sessionId,如果已包含則說明以前已經為此客戶端創建過session,服務
器就按照sessionId把這個session檢索出來使用(檢索不到,會新建一個),如果客戶端請求不包含sessionId,則為此客戶端創建一個session並且生成一個與此session相關
聯的sessionId,sessionId的值是一個既不會重復,又不容易被找到規律以仿造的字符串,這個sessionId將被在本次響應中返回給客戶端保存。
禁用cookie:
如果客戶端禁用了cookie,通常有兩種方法實現session而不依賴cookie。
-
URL重寫,就是把sessionId直接附加在URL路徑的后面。
-
表單隱藏字段。就是服務器會自動修改表單,添加一個隱藏字段,以便在表單提交時能夠把session id傳遞回服務器。比如:
<form name="testform" action="/xxx"> <input type="hidden" name="jsessionid" value="ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764"> <input type="text"> </form>
Session共享:
對於多網站(同一父域不同子域)單服務器,我們需要解決的就是來自不同網站之間SessionId的共享。由於域名不同(aaa.test.com和bbb.test.com),而SessionId又分別儲存
在各自的cookie中,因此服務器會認為對於兩個子站的訪問,是來自不同的會話。解決的方法是通過修改cookies的域名為父域名達到cookie共享的目的,從而實現SessionId的共
享。帶來的弊端就是,子站間的cookie信息也同時被共享了。
cookie和session案例解析(記住我)
package servlet; import domain.User; 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.util.List; /* * 用戶登錄的Servlet * */ @WebServlet("/login") public class LoginServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding("utf-8"); resp.setCharacterEncoding("utf-8"); //接收數據 String username = req.getParameter("username"); String password = req.getParameter("password"); //從ServletContext域中獲得保存用戶信息集合 List<User> list = (List<User>) this.getServletContext().getAttribute("list"); for (User user:list){ //判斷用戶名是否正確 if (username.equals(user.getUsername())){ //判斷密碼是否正確 if (password.equals(user.getPassword())){ //用戶名密碼都正確 //登錄成功 //判斷記住用戶名復選框是否勾選 String remember = req.getParameter("remember"); if ("true".equals(remember)){ //完成記住用戶名的功能 Cookie cookie = new Cookie("username",username); //設置有效路徑 cookie.setPath("/login.jsp");//設置此路徑后只能在login.jsp訪問cookie //設置有效時間 cookie.setMaxAge(60*60*24); //將cookie回寫到瀏覽器 resp.addCookie(cookie); } //將用戶的信息保存到Session中 req.getSession().setAttribute("user",user); resp.sendRedirect("/success.jsp"); return; } } } //登錄失敗 req.setAttribute("msg","用戶名或密碼錯誤!"); req.getRequestDispatcher("/login.jsp").forward(req,resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req, resp); } }
package utils; import javax.servlet.http.Cookie; public class CookieUtils { public static Cookie findCookie(Cookie[] cookies,String name){ if (cookies==null){ //說明客戶端沒有攜帶Cookie return null; }else { //說明客戶端攜帶Cookie for (Cookie cookie:cookies){ if (name.equals(cookie.getName())){ return cookie; } } return null; } } }
<%@page import="utils.CookieUtils"%> <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>登錄頁面</title> <link rel="stylesheet" href="./css/login.css"> </head> <body> <div class="login"> <div class="header"> <h1> <a href="/login.jsp">登錄</a> <a href="/regist.jsp">注冊</a> </h1> </div> <% String username=""; //獲得從客戶端攜帶過來的所有的Cookie Cookie[] cookies = request.getCookies(); // 從Cookie的數組中查找指定名稱的Cookie Cookie cookie = CookieUtils.findCookie(cookies, "username"); if(cookie != null){ username = cookie.getValue(); } if(session.getAttribute("username")!=null){ username = (String)session.getAttribute("username"); } String msg = ""; if(request.getAttribute("msg")!=null){ msg = (String)request.getAttribute("msg"); } %> <h3><%=msg %></h3> <form action="/login" method="post"> <table> <tr> <td class="td1">用戶名</td> <td><input type="text" class="input1" name="username" value="<%=username %>"></td> </tr> <tr> <td class="td1">密碼</td> <td><input type="password" class="input1" name="password"></td> </tr> <tr> <td class="td1" colspan="2"> <input type="checkbox" name="remember" value="true" checked="checked"> 記住用戶名</td> </tr> <tr> <td colspan="2"> <div class="btn-red"> <input type="submit" value="登錄" id="login-btn"> </div> </td> </tr> </table> </form> </div> </body> </html>
完整案例請看《帶新手玩轉MVC》
Cookie知識拓展
設置Cookie的所有屬性
除了name與value之外,Cookie還具有其他幾個常用的屬性。每個屬性對應一個getter方法與一個setter方法。
Cookie常用屬性
Cookie的有效期
Cookie的maxAge決定着Cookie的有效期,單位為秒(Second)。Cookie中通過getMaxAge()方法與setMaxAge(int maxAge)方法來讀寫maxAge屬性。
如果maxAge屬性為正數,則表示該Cookie會在maxAge秒之后自動失效。瀏覽器會將maxAge為正數的 Cookie持久化,即寫到對應的Cookie文件中。無論客戶關閉了瀏覽器還是電腦,只要還在maxAge秒之前,登錄網站時該Cookie仍然有效。 下面代碼中的Cookie信息將永遠有效。
Cookie cookie = new Cookie("username","helloweenvsfei"); // 新建Cookie cookie.setMaxAge(Integer.MAX_VALUE); // 設置生命周期為MAX_VALUE response.addCookie(cookie); // 輸出到客戶端
如果maxAge為負數,則表示該Cookie僅在本瀏覽器窗口以及本窗口打開的子窗口內有效,關閉窗口后該 Cookie即失效。maxAge為負數的Cookie,為臨時性Cookie,不會被持久化,不會被寫到Cookie文件中。Cookie信息保存在瀏 覽器內存中,因此關閉瀏覽器該Cookie就消失了。Cookie默認的maxAge值為–1。
如果maxAge為0,則表示刪除該Cookie。Cookie機制沒有提供刪除Cookie的方法,因此通過設置該Cookie即時失效實現刪除Cookie的效果。失效的Cookie會被瀏覽器從Cookie文件或者內存中刪除,
例如:
Cookie cookie = new Cookie("username","helloweenvsfei"); // 新建Cookie cookie.setMaxAge(0); // 設置生命周期為0,不能為負數 response.addCookie(cookie); // 必須執行這一句
response對象提供的Cookie操作方法只有一個添加操作add(Cookie cookie)。
要想修改Cookie只能使用一個同名的Cookie來覆蓋原來的Cookie,達到修改的目的。刪除時只需要把maxAge修改為0即可。
注意:從客戶端讀取Cookie時,包括maxAge在內的其他屬性都是不可讀的,也不會被提交。瀏覽器提交Cookie時只會提交name與value屬性。maxAge屬性只被瀏覽器用來判斷Cookie是否過期。
Cookie的修改、刪除
Cookie並不提供修改、刪除操作。如果要修改某個Cookie,只需要新建一個同名的Cookie,添加到response中覆蓋原來的Cookie。
如果要刪除某個Cookie,只需要新建一個同名的Cookie,並將maxAge設置為0,並添加到response中覆蓋原來的Cookie。注意是0而不是負數。負數代表其他的意義。讀者可以通過上例的程序進行驗證,設置不同的屬性。
注意:修改、刪除Cookie時,新建的Cookie除value、maxAge之外的所有屬性,例如name、path、domain等,都要與原Cookie完全一樣。否則,瀏覽器將視為兩個不同的Cookie不予覆蓋,導致修改、刪除失敗。
Cookie的域名
Cookie是不可跨域名的。域名www.google.com頒發的Cookie不會被提交到域名www.baidu.com去。這是由Cookie的隱私安全機制決定的。隱私安全機制能夠禁止網站非法獲取其他網站的Cookie。
正常情況下,同一個一級域名下的兩個二級域名如www.helloweenvsfei.com和 images.helloweenvsfei.com也不能交互使用Cookie,因為二者的域名並不嚴格相同。如果想所有 helloweenvsfei.com名下的二級域名都可以使用該Cookie,需要設置Cookie的domain參數,例如:
Cookie cookie = new Cookie("time","20080808"); // 新建Cookie
cookie.setDomain(".helloweenvsfei.com"); // 設置域名
cookie.setPath("/"); // 設置路徑
cookie.setMaxAge(Integer.MAX_VALUE); // 設置有效期
response.addCookie(cookie); // 輸出到客戶端
讀者可以修改本機C:\WINDOWS\system32\drivers\etc下的hosts文件來配置多個臨時域名,然后使用setCookie.jsp程序來設置跨域名Cookie驗證domain屬性。
注意:domain參數必須以點(".")開始。另外,name相同但domain不同的兩個Cookie是兩個不同的Cookie。如果想要兩個域名完全不同的網站共有Cookie,可以生成兩個Cookie,domain屬性分別為兩個域名,輸出到客戶端。
Cookie的路徑
domain屬性決定運行訪問Cookie的域名,而path屬性決定允許訪問Cookie的路徑(ContextPath)。例如,如果只允許/sessionWeb/下的程序使用Cookie,可以這么寫:
Cookie cookie = new Cookie("time","20080808"); // 新建Cookie
cookie.setPath("/session/"); // 設置路徑
response.addCookie(cookie); // 輸出到客戶端
設置為“/”時允許所有路徑使用Cookie。path屬性需要使用符號“/”結尾。name相同但domain相同的兩個Cookie也是兩個不同的Cookie。
注意:頁面只能獲取它屬於的Path的Cookie。例如/session/test/a.jsp不能獲取到路徑為/session/abc/的Cookie。使用時一定要注意。
cookie的不可跨域性
很多網站都會使用Cookie。例如,Google會向客戶端頒發Cookie,Baidu也會向客戶端頒發Cookie。那瀏覽器訪問Google會不會也攜帶上Baidu頒發的Cookie呢?或者Google能不能修改Baidu頒發的Cookie呢?
答案是否定的。Cookie具有不可跨域名性。根據Cookie規范,瀏覽器訪問Google只會攜帶Google的Cookie,而不會攜帶Baidu的Cookie。Google也只能操作Google的Cookie,而不能操作Baidu的Cookie。
Cookie在客戶端是由瀏覽器來管理的。瀏覽器能夠保證Google只會操作Google的Cookie而不會操作 Baidu的Cookie,從而保證用戶的隱私安全。瀏覽器判斷一個網站是否能操作另一個網站Cookie的依據是域名。Google與Baidu的域名 不一樣,因此Google不能操作Baidu的Cookie。
需要注意的是,雖然網站images.google.com與網站www.google.com同屬於Google,但是域名不一樣,二者同樣不能互相操作彼此的Cookie。
Unicode編碼:保存中文
中文與英文字符不同,中文屬於Unicode字符,在內存中占4個字符,而英文屬於ASCII字符,內存中只占2個字節。Cookie中使用Unicode字符時需要對Unicode字符進行編碼,否則會亂碼。
提示:Cookie中保存中文只能編碼。一般使用UTF-8編碼即可。不推薦使用GBK等中文編碼,因為瀏覽器不一定支持,而且JavaScript也不支持GBK編碼。
BASE64編碼:保存二進制圖片
Cookie不僅可以使用ASCII字符與Unicode字符,還可以使用二進制數據。例如在Cookie中使用數字證書,提供安全度。使用二進制數據時也需要進行編碼。
*注意:本程序僅用於展示Cookie中可以存儲二進制內容,並不實用。由於瀏覽器每次請求服務器都會攜帶Cookie,因此Cookie內容不宜過多,否則影響速度。Cookie的內容應該少而精。
Cookie的安全屬性
HTTP協議不僅是無狀態的,而且是不安全的。使用HTTP協議的數據不經過任何加密就直接在網絡上傳播,有被截獲的可 能。使用HTTP協議傳輸很機密的內容是一種隱患。如果不希望Cookie在HTTP等非安全協議中傳輸,可以設置Cookie的secure屬性為 true。瀏覽器只會在HTTPS和SSL等安全協議中傳輸此類Cookie。下面的代碼設置secure屬性為true:
Cookie cookie = new Cookie("time", "20080808"); // 新建Cookie cookie.setSecure(true); // 設置安全屬性 response.addCookie(cookie); // 輸出到客戶端
提示:secure屬性並不能對Cookie內容加密,因而不能保證絕對的安全性。如果需要高安全性,需要在程序中對Cookie內容加密、解密,以防泄密。
JavaScript操作Cookie
Cookie是保存在瀏覽器端的,因此瀏覽器具有操作Cookie的先決條件。瀏覽器可以使用腳本程序如 JavaScript或者VBScript等操作Cookie。這里以JavaScript為例介紹常用的Cookie操作。例如下面的代碼會輸出本頁面 所有的Cookie。
由於JavaScript能夠任意地讀寫Cookie,有些好事者便想使用JavaScript程序去窺探用戶在其他網 站的Cookie。不過這是徒勞的,W3C組織早就意識到JavaScript對Cookie的讀寫所帶來的安全隱患並加以防備了,W3C標准的瀏覽器會 阻止JavaScript讀寫任何不屬於自己網站的Cookie。換句話說,A網站的JavaScript程序讀寫B網站的Cookie不會有任何結果。
案例:永久登錄
如果用戶是在自己家的電腦上上網,登錄時就可以記住他的登錄信息,下次訪問時不需要再次登錄,直接訪問即可。實現方法是把登錄信息如賬號、密碼等保存在Cookie中,並控制Cookie的有效期,下次訪問時再驗證Cookie中的登錄信息即可。
保存登錄信息有多種方案。最直接的是把用戶名與密碼都保持到Cookie中,下次訪問時檢查Cookie中的用戶名與密碼,與數據庫比較。這是一種比較危險的選擇,一般不把密碼等重要信息保存到Cookie中。
還有一種方案是把密碼加密后保存到Cookie中,下次訪問時解密並與數據庫比較。這種方案略微安全一些。如果不希望保存密碼,還可以把登錄的時間戳保存到Cookie與數據庫中,到時只驗證用戶名與登錄時間戳就可以了。
這幾種方案驗證賬號時都要查詢數據庫。
本例將采用另一種方案,只在登錄時查詢一次數據庫,以后訪問驗證登錄信息時不再查詢數據庫。實現方式是把賬號按照一定的規則加密后,連同賬號一塊保存到Cookie中。下次訪問時只需要判斷賬號的加密規則是否正確即可。本例把賬號保存到名為account的Cookie中,把賬號連同密鑰用MD1算法加密后保存到名為ssid的Cookie中。驗證時驗證Cookie中的賬號與密鑰加密后是否與Cookie中的ssid相等。相關代碼如下:
代碼1.8 loginCookie.jsp

1 <%@ page language="java"pageEncoding="UTF-8" isErrorPage="false" %> 2 3 <%! // JSP方法 4 5 private static final String KEY =":cookie@helloweenvsfei.com"; 6 // 密鑰 7 8 public final static String calcMD1(Stringss) { // MD1 加密算法 9 10 String s = ss==null ?"" : ss; // 若為null返回空 11 12 char hexDigits[] = { '0','1', '2', '3', '4', '1', '6', '7', '8', '9', 13 'a', 'b', 'c', 'd', 'e', 'f' }; // 字典 14 15 try { 16 17 byte[] strTemp =s.getBytes(); // 獲取字節 18 19 MessageDigestmdTemp = MessageDigest.getInstance("MD1"); // 獲取MD1 20 21 mdTemp.update(strTemp); // 更新數據 22 23 byte[] md =mdTemp.digest(); // 加密 24 25 int j =md.length; // 加密后的長度 26 27 char str[] = newchar[j * 2]; // 新字符串數組 28 29 int k =0; // 計數器k 30 31 for (int i = 0; i< j; i++) { // 循環輸出 32 33 byte byte0 =md[i]; 34 35 str[k++] =hexDigits[byte0 >>> 4 & 0xf]; 36 37 str[k++] =hexDigits[byte0 & 0xf]; 38 39 } 40 41 return newString(str); // 加密后字符串 42 43 } catch (Exception e){return null; } 44 45 } 46 47 %> 48 49 <% 50 51 request.setCharacterEncoding("UTF-8"); // 設置request編碼 52 53 response.setCharacterEncoding("UTF-8"); // 設置response編碼 54 55 56 57 String action =request.getParameter("action"); // 獲取action參數 58 59 60 61 if("login".equals(action)){ // 如果為login動作 62 63 String account =request.getParameter("account"); 64 // 獲取account參數 65 66 String password =request.getParameter("password"); 67 // 獲取password參數 68 69 int timeout = newInteger(request.getParameter("timeout")); 70 // 獲取timeout參數 71 72 73 74 String ssid =calcMD1(account + KEY); // 把賬號、密鑰使用MD1加密后保存 75 76 77 78 CookieaccountCookie = new Cookie("account", account); 79 // 新建Cookie 80 81 accountCookie.setMaxAge(timeout); // 設置有效期 82 83 84 85 Cookie ssidCookie =new Cookie("ssid", ssid); // 新建Cookie 86 87 ssidCookie.setMaxAge(timeout); // 設置有效期 88 89 90 91 response.addCookie(accountCookie); // 輸出到客戶端 92 93 response.addCookie(ssidCookie); // 輸出到客戶端 94 95 96 97 // 重新請求本頁面,參數中帶有時間戳,禁止瀏覽器緩存頁面內容 98 99 response.sendRedirect(request.getRequestURI() + "?" + System. 100 currentTimeMillis()); 101 102 return; 103 104 } 105 106 elseif("logout".equals(action)){ // 如果為logout動作 107 108 109 110 CookieaccountCookie = new Cookie("account", ""); 111 // 新建Cookie,內容為空 112 113 accountCookie.setMaxAge(0); // 設置有效期為0,刪除 114 115 116 117 Cookie ssidCookie =new Cookie("ssid", ""); // 新建Cookie,內容為空 118 119 ssidCookie.setMaxAge(0); // 設置有效期為0,刪除 120 121 response.addCookie(accountCookie); // 輸出到客戶端 122 123 response.addCookie(ssidCookie); // 輸出到客戶端 124 125 //重新請求本頁面,參數中帶有時間戳,禁止瀏覽器緩存頁面內容 126 127 response.sendRedirect(request.getRequestURI() + "?" + System. 128 currentTimeMillis()); 129 130 return; 131 132 } 133 134 boolean login = false; // 是否登錄 135 136 String account = null; // 賬號 137 138 String ssid = null; // SSID標識 139 140 141 142 if(request.getCookies() !=null){ // 如果Cookie不為空 143 144 for(Cookie cookie :request.getCookies()){ // 遍歷Cookie 145 146 if(cookie.getName().equals("account")) // 如果Cookie名為 147 account 148 149 account = cookie.getValue(); // 保存account內容 150 151 if(cookie.getName().equals("ssid")) // 如果為SSID 152 153 ssid = cookie.getValue(); // 保存SSID內容 154 155 } 156 157 } 158 159 if(account != null && ssid !=null){ // 如果account、SSID都不為空 160 161 login =ssid.equals(calcMD1(account + KEY)); 162 // 如果加密規則正確, 則視為已經登錄 163 164 } 165 166 %> 167 168 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01Transitional//EN"> 169 170 <legend><%= login ? "歡迎您回來" : "請先登錄"%></legend> 171 172 <% if(login){%> 173 174 歡迎您, ${ cookie.account.value }. 175 176 <a href="${ pageContext.request.requestURI }?action=logout"> 177 注銷</a> 178 179 <% } else {%> 180 181 <formaction="${ pageContext.request.requestURI }?action=login" 182 method="post"> 183 184 <table> 185 186 <tr><td>賬號: </td> 187 188 <td><input type="text"name="account" style="width: 189 200px; "></td> 190 191 </tr> 192 193 <tr><td>密碼: </td> 194 195 <td><inputtype="password" name="password"></td> 196 197 </tr> 198 199 <tr> 200 201 <td>有效期: </td> 202 203 <td><inputtype="radio" name="timeout" value="-1" 204 checked> 關閉瀏覽器即失效 <br/> <input type="radio" 205 name="timeout" value="<%= 30 *24 * 60 * 60 %>"> 30天 206 內有效 <br/><input type="radio" name="timeout" value= 207 "<%= Integer.MAX_VALUE %>"> 永久有效 <br/> </td> </tr> 208 209 <tr><td></td> 210 211 <td><input type="submit"value=" 登 錄 " class= 212 "button"></td> 213 214 </tr> 215 216 </table> 217 218 </form> 219 220 <% } %>
登錄時可以選擇登錄信息的有效期:關閉瀏覽器即失效、30天內有效與永久有效。通過設置Cookie的age屬性來實現,注意觀察代碼
提示:該加密機制中最重要的部分為算法與密鑰。由於MD1算法的不可逆性,即使用戶知道了賬號與加密后的字符串,也不可能解密得到密鑰。因此,只要保管好密鑰與算法,該機制就是安全的。
*****************************************************************************************************
我的博客園地址:https://www.cnblogs.com/zyx110/