1. 程式人生 > >cookie、session和application都是些什麼神?——圖文加案例,不怕你不會,就怕你不看

cookie、session和application都是些什麼神?——圖文加案例,不怕你不會,就怕你不看

cookie、session和application都是些什麼神?

前言:

一直想寫一篇關於cookie和session的部落格,由於種種原因,一直沒有整理,這不,今天還就遇到問題了,之前雖然會,但是好久沒用又給忘了,結果還得查資料。是時候填坑了,閒話少說,開幹。

 

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。

 

 

思考兩個問題:

  1. 什麼東西可以讓你每次請求都把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採用的就是這種方式,但是也可以替換成其他形式。

  1. 如何儲存需要的資訊?

    伺服器通過SessionId作為key,讀寫到對應的value,這就達到了保持會話資訊的目的。

session的建立:

當程式需要為某個客戶端的請求建立一個session時,伺服器首先檢查這個客戶端的請求裡是否已包含了sessionId,如果已包含則說明以前已經為此客戶端建立過session,服務

器就按照sessionId把這個session檢索出來使用(檢索不到,會新建一個),如果客戶端請求不包含sessionId,則為此客戶端建立一個session並且生成一個與此session相關

聯的sessionId,sessionId的值是一個既不會重複,又不容易被找到規律以仿造的字串,這個sessionId將被在本次響應中返回給客戶端儲存。

禁用cookie:

  如果客戶端禁用了cookie,通常有兩種方法實現session而不依賴cookie。

  1. URL重寫,就是把sessionId直接附加在URL路徑的後面。

  2. 表單隱藏欄位。就是伺服器會自動修改表單,新增一個隱藏欄位,以便在表單提交時能夠把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 =":[email protected]";
  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 }. &nbsp;&nbsp;
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 ​        <% } %>
View Code

 

登入時可以選擇登入資訊的有效期:關閉瀏覽器即失效、30天內有效與永久有效。通過設定Cookie的age屬性來實現,注意觀察程式碼

提示:該加密機制中最重要的部分為演算法與金鑰。由於MD1演算法的不可逆性,即使使用者知道了賬號與加密後的字串,也不可能解密得到金鑰。因此,只要保管好金鑰與演算法,該機制就是安全的。

 

 

                                                                    

 

*****************************************************************************************************

我的部落格園地址:https://www.cnblogs.com/zyx110/

【原創宣告】此篇為作者原創,未經本人同意不得轉載,經本人同意轉載請說明出處。

我不能保證我所說的都是對的,但我能保證每一篇都是用心去寫的,我始終認同“分享的越多,你的價值增值越大”,歡迎大家關注我的技術分享“Java匹馬行天下”和學習心得分享“匹馬行天下”,在分享中進步,越努力越幸運,人生贏在轉折處,改變從現在開始!

支援我的朋友們記得點波推薦哦,您的肯定就是我前進的動