Servlet第六篇【Session介紹、API、生命周期、應用、與Cookie區別】

来源:https://www.cnblogs.com/Java3y/archive/2018/02/04/8414360.html
-Advertisement-
Play Games

什麼是Session Session 是另一種記錄瀏覽器狀態的機制。不同的是Cookie保存在瀏覽器中,Session保存在伺服器中。用戶使用瀏覽器訪問伺服器的時候,伺服器把用戶的信息以某種的形式記錄在伺服器,這就是Session 如果說Cookie是檢查用戶身上的”通行證“來確認用戶的身份,那麼S ...


什麼是Session

Session 是另一種記錄瀏覽器狀態的機制。不同的是Cookie保存在瀏覽器中,Session保存在伺服器中。用戶使用瀏覽器訪問伺服器的時候,伺服器把用戶的信息以某種的形式記錄在伺服器,這就是Session

如果說Cookie是檢查用戶身上的”通行證“來確認用戶的身份,那麼Session就是通過檢查伺服器上的”客戶明細表“來確認用戶的身份的。Session相當於在伺服器中建立了一份“客戶明細表”。

為什麼要使用Session技術?

Session比Cookie使用方便,Session可以解決Cookie解決不了的事情【Session可以存儲對象,Cookie只能存儲字元串。】。

Session API

  • long getCreationTime();【獲取Session被創建時間】
  • String getId();【獲取Session的id】
  • long getLastAccessedTime();【返回Session最後活躍的時間】
  • ServletContext getServletContext();【獲取ServletContext對象】
  • void setMaxInactiveInterval(int var1);【設置Session超時時間】
  • int getMaxInactiveInterval();【獲取Session超時時間】
  • Object getAttribute(String var1);【獲取Session屬性
  • Enumeration
  • void setAttribute(String var1, Object var2);【設置Session屬性】
  • void removeAttribute(String var1);【移除Session屬性】
  • void invalidate();【銷毀該Session】
  • boolean isNew();【該Session是否為新的】

Session作為域對象

從上面的API看出,Session有著request和ServletContext類似的方法。其實Session也是一個域對象。Session作為一種記錄瀏覽器狀態的機制,只要Session對象沒有被銷毀,Servlet之間就可以通過Session對象實現通訊

  • 我們來試試吧,在Servlet4中設置Session屬性

        //得到Session對象
        HttpSession httpSession = request.getSession();
        
        //設置Session屬性
        httpSession.setAttribute("name", "看完博客就要點贊!!");
  • 在Servlet5中獲取到Session存進去的屬性

        //獲取到從Servlet4的Session存進去的值
        HttpSession httpSession = request.getSession();
        String value = (String) httpSession.getAttribute("name");
        System.out.println(value);
  • 訪問Servlet4,再訪問Servlet5

  • 一般來講,當我們要存進的是用戶級別的數據就用Session,那什麼是用戶級別呢?只要瀏覽器不關閉,希望數據還在,就使用Session來保存

Session的生命周期和有效期

  • Session在用戶第一次訪問伺服器Servlet,jsp等動態資源就會被自動創建,Session對象保存在記憶體里,這也就為什麼上面的例子可以直接使用request對象獲取得到Session對象
  • 如果訪問HTML,IMAGE等靜態資源Session不會被創建。
  • Session生成後,只要用戶繼續訪問,伺服器就會更新Session的最後訪問時間,無論是否對Session進行讀寫,伺服器都會認為Session活躍了一次
  • 由於會有越來越多的用戶訪問伺服器,因此Session也會越來越多。為了防止記憶體溢出,伺服器會把長時間沒有活躍的Session從記憶體中刪除,這個時間也就是Session的超時時間
  • Session的超時時間預設是30分鐘,有三種方式可以對Session的超時時間進行修改

  • 第一種方式:在tomcat/conf/web.xml文件中設置,時間值為20分鐘,所有的WEB應用都有效

            
            <session-config>
                <session-timeout>20</session-timeout>
            </session-config>   

  • 第二種方式:在單個的web.xml文件中設置,對單個web應用有效,如果有衝突,以自己的web應用為準
            
            <session-config>
                <session-timeout>20</session-timeout>
            </session-config>   
  • 第三種方式:通過setMaxInactiveInterval()方法設置

        //設置Session最長超時時間為60秒,這裡的單位是秒
        httpSession.setMaxInactiveInterval(60);

        System.out.println(httpSession.getMaxInactiveInterval());

  • Session的有效期與Cookie的是不同的


使用Session完成簡單的購物功能

  • 我們還是以書籍為例,所以可以copy“顯示瀏覽過的商品“例子部分的代碼。

        response.setContentType("text/html;charset=UTF-8");
        PrintWriter printWriter = response.getWriter();

        printWriter.write("網頁上所有的書籍:" + "<br/>");

        //拿到資料庫所有的書
        LinkedHashMap<String, Book> linkedHashMap = DB.getAll();
        Set<Map.Entry<String, Book>> entry = linkedHashMap.entrySet();

        //顯示所有的書到網頁上
        for (Map.Entry<String, Book> stringBookEntry : entry) {

            Book book = stringBookEntry.getValue();

            String url = "/ouzicheng/Servlet6?id=" + book.getId();
            printWriter.write(book.getName());
            printWriter.write("<a href='" + url + "'>購買</a>");
            printWriter.write("<br/>");
        }
  • 在購物車頁面上,獲取到用戶想買的書籍【用戶可能不單想買一本書,於是乎,就用一個List容器裝載書籍】,有了:先遍歷Cookie,再判斷是否是第一次訪問Servlet的邏輯思路,我們就可以先獲取到Session的屬性,如果Session的屬性為null,那麼就是還沒有該屬性

        //得到用戶想買書籍的id
        String id = request.getParameter("id");

        //根據書籍的id找到用戶想買的書
        Book book = (Book) DB.getAll().get(id);

        //獲取到Session對象
        HttpSession httpSession = request.getSession();

        //由於用戶可能想買多本書的,所以我們用一個容器裝著書籍
        List list = (List) httpSession.getAttribute("list");
        if (list == null) {
            
            list = new ArrayList();
            
            //設置Session屬性
            httpSession.setAttribute("list",list);
        }
        //把書籍加入到list集合中
        list.add(book);
  • 按我們正常的邏輯思路:先創建一個ArrayList對象,把書加到list集合中,然後設置Session的屬性。這樣是行不通的。每次Servlet被訪問的時候都會創建一個ArrayList集合,書籍會被分發到不同的ArrayList中去。所以下麵的代碼是不行的!

        //得到用戶想買書籍的id
        String id = request.getParameter("id");

        //根據書籍的id找到用戶想買的書
        Book book = (Book) DB.getAll().get(id);

        //獲取到Session對象
        HttpSession httpSession = request.getSession();

        //創建List集合
        List list = new ArrayList();
        list.add(book);

        httpSession.setAttribute("list", list);
  • 既然用戶已經購買了書籍,那麼也應該給提供頁面顯示用戶購買過哪些書籍

        //得到用戶想買書籍的id
        String id = request.getParameter("id");

        //根據書籍的id找到用戶想買的書
        Book book = (Book) DB.getAll().get(id);

        //獲取到Session對象
        HttpSession httpSession = request.getSession();

        //由於用戶可能想買多本書的,所以我們用一個容器裝著書籍
        List list = (List) httpSession.getAttribute("list");
        if (list == null) {

            list = new ArrayList();

            //設置Session屬性
            httpSession.setAttribute("list",list);
        }
        //把書籍加入到list集合中
        list.add(book);

        String url = "/ouzicheng/Servlet7";
        response.sendRedirect(url);
  • 列出用戶購買過的書籍

        //要得到用戶購買過哪些書籍,得到Session的屬性遍歷即可
        HttpSession httpSession = request.getSession();
        List<Book> list = (List) httpSession.getAttribute("list");

        if (list == null || list.size() == 0) {
            printWriter.write("對不起,你還沒有買過任何商品");

        } else {
            printWriter.write("您購買過以下商品:");
            printWriter.write("<br/>");
            for (Book book : list) {
                printWriter.write(book.getName());
                printWriter.write("<br/>");
            }
        }
  • 效果如下


Session的實現原理

  • 用現象說明問題,我在Servlet4中的代碼設置了Session的屬性

        //得到Session對象
        HttpSession httpSession = request.getSession();

        //設置Session屬性
        httpSession.setAttribute("name", "看完博客就要點贊!!");
  • 接著在Servlet7把Session的屬性取出來

        String value = (String) request.getSession().getAttribute("name");

        printWriter.write(value);
  • 自然地,我們能取到在Servlet4中Session設置的屬性

  • 接著,我在瀏覽器中新建一個會話,再次訪問Servlet7

  • 發現報了空指針異常的錯誤

  • 現在問題來了:伺服器是如何實現一個session為一個用戶瀏覽器服務的?換個說法:為什麼伺服器能夠為不同的用戶瀏覽器提供不同session?
  • HTTP協議是無狀態的,Session不能依據HTTP連接來判斷是否為同一個用戶。於是乎:伺服器向用戶瀏覽器發送了一個名為JESSIONID的Cookie,它的值是Session的id值。其實Session依據Cookie來識別是否是同一個用戶。
  • 簡單來說:Session 之所以可以識別不同的用戶,依靠的就是Cookie
  • 該Cookie是伺服器自動頒發給瀏覽器的,不用我們手工創建的。該Cookie的maxAge值預設是-1,也就是說僅當前瀏覽器使用,不將該Cookie存在硬碟中

  • 我們來捋一捋思路流程:當我們訪問Servlet4的時候,伺服器就會創建一個Session對象,執行我們的程式代碼,並自動頒發個Cookie給用戶瀏覽器

  • 當我們用同一個瀏覽器訪問Servlet7的時候,瀏覽器會把Cookie的值通過http協議帶過去給伺服器,伺服器就知道用哪一Session

  • 而當我們使用新會話的瀏覽器訪問Servlet7的時候該新瀏覽器並沒有Cookie,伺服器無法辨認使用哪一個Session,所以就獲取不到值

瀏覽器禁用了Cookie,Session還能用嗎?

上面說了Session是依靠Cookie來識別用戶瀏覽器的。如果我的用戶瀏覽器禁用了Cookie了呢?絕大多數的手機瀏覽器都不支持Cookie,那我的Session怎麼辦?

  • 好的,我們來看看情況是怎麼樣的。用戶瀏覽器訪問Servlet4的時候,伺服器向用戶瀏覽器頒發了一個Cookie

  • 但是呢,當用戶瀏覽器訪問Servlet7的時候,由於我們禁用了Cookie,所以用戶瀏覽器並沒有把Cookie帶過去給伺服器

  • 一看,Session好像不能用了。但是Java Web提供瞭解決方法:URL地址重寫

  • HttpServletResponse類提供了兩個URL地址重寫的方法:
    • encodeURL(String url)
    • encodeRedirectURL(String url)
  • 需要值得註意的是:這兩個方法會自動判斷該瀏覽器是否支持Cookie,如果支持Cookie,重寫後的URL地址就不會帶有jsessionid了【當然了,即使瀏覽器支持Cookie,第一次輸出URL地址的時候還是會出現jsessionid(因為沒有任何Cookie可帶)】

  • 下麵我們就以上面“購物”的例子來做試驗吧!首先我們來看看禁用掉Cookie對原來的小例子有什麼影響

  • 訪問Servlet1,隨便點擊一本書籍購買

  • 無論點擊多少次,都會直接提示我們有買過任何商品

  • 原因也非常簡單,沒有Cookie傳遞給伺服器,伺服器每次創建的時候都是新的Session,導致最後獲取到的List集合一定是空的。

  • 不同Servlet獲取到的Session的id號都是不同的

  • 下麵我們就對URL進行重寫,看看能不能恢覆沒有禁掉Cookie之前的效果。
  • 原則:把Session的屬性帶過去【傳遞給】另外一個Servlet,都要URL地址重寫

  • 在跳轉到顯示購買過商品的Servlet的時候,URL地址重寫。


        String url = "/ouzicheng/Servlet7";

        response.sendRedirect(response.encodeURL(url));
  • 再次訪問Servlet1,當我點擊javaweb的時候,已經能夠成功出現我買過的商品了。並且Session的id通過URL地址重寫,使用的是同一個Session

  • URL地址重寫的原理:將Session的id信息重寫到URL地址中伺服器解析重寫後URL,獲取Session的id。這樣一來,即使瀏覽器禁用掉了Cookie,但Session的id通過伺服器端傳遞,還是可以使用Session來記錄用戶的狀態。

Session禁用Cookie

  • Java Web規範支持通過配置禁用Cookie

  • 禁用自己項目的Cookie
    • 在META-INF文件夾下的context.xml文件中修改(沒有則創建)

    
            <?xml version='1.0' encoding='utf-8'?>
    
            <Context path="/ouzicheng" cookies="false">
            </Context>
  • 禁用全部web應用的Cookie
    • 在conf/context.xml中修改

註意:該配置只是讓伺服器不能自動維護名為jsessionid的Cookie,並不能阻止Cookie的讀寫。


Session案例

使用Session完成用戶簡單登陸

  • 先創建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方法
  • 使用簡單的集合模擬一個資料庫


    private static List<User> list = new ArrayList<>();

    //裝載些數據進資料庫
    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寫太麻煩了!

<form action="/ouzicheng/LoginServlet" method="post">
    用戶名:<input type="text" name="username"><br/>
    密碼:<input type="password" name="password"><br/>
    <input type="submit" value="提交">

</form>
  • 獲取到表單提交的數據,查找資料庫是否有相對應的用戶名和密碼。如果沒有就提示用戶名或密碼出錯了,如果有就跳轉到另外一個頁面


        String username = request.getParameter("username");
        String password = request.getParameter("password");

        User user = UserDB.find(username, password);

        //如果找不到,就是用戶名或密碼出錯了。
        if (user == null) {
            response.getWriter().write("you can't login");
            return;
        }

        //標記著該用戶已經登陸了!
        HttpSession httpSession = request.getSession();
        httpSession.setAttribute("user", user);

        //跳轉到其他頁面,告訴用戶成功登陸了。
        response.sendRedirect(response.encodeURL("index.jsp"));
  • 我們來試試下資料庫沒有的用戶名和密碼,提示我不能登陸。

  • 試試資料庫存在的用戶名和密碼


利用Session防止表單重覆提交

  • 重覆提交的危害:
    • 在投票的網頁上不停地提交,實現了刷票的效果。
    • 註冊多個用戶,不斷發帖子,擾亂正常發帖秩序。
  • 首先我們來看一下常見的重覆提交。
    • 在處理表單的Servlet中刷新。
    • 後退再提交
    • 網路延遲,多次點擊提交按鈕
  • 下麵的gif是後退再提交在處理提交請求的Servlet中刷新

  • 下麵的gif是網路延遲,多次點擊提交按鈕

  • 對於網路延遲造成的多次提交數據給伺服器,其實是客戶端的問題。於是,我們可以使用javaScript來防止這種情況
  • 要做的事情也非常簡單:當用戶第一次點擊提交按鈕時,把數據提交給伺服器。當用戶再次點擊提交按鈕時,就不把數據提交給伺服器了。

  • 監聽用戶提交事件。只能讓用戶提交一次表單!


<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>表單提交</title>

    <script type="text/javascript">

        //定義一個全局標識量:是否已經提交過表單數據
        var isCommitted = false;

        function doSubmit() {

            //false表示的是沒有提交過,於是就可以讓表單提交給Servlet
            if(isCommitted==false) {

                isCommitted = true;
                return true;
            }else {
                return false;
            }
        }
    </script>
</head>
<body>

<form action="/ouzicheng/Servlet7" onsubmit="return doSubmit()">

    用戶名:<input type="text" name="username">
    <input type="submit" value="提交">
</form>

</body>
</html>
  • 好的,我們來試一下是不是真的可以解決網路延遲所造成的多次提交表單數據,註意滑鼠,我已經點擊過很多次的了!

  • 由於網路延遲造成的多次提交數據給伺服器,我們還可以使用javaScript代碼這樣解決:當我點擊過一次提交按鈕時,我就把提交的按鈕隱藏起來。不能讓用戶點擊了

  • 想要讓按鈕隱藏起來,也很簡單。只要獲取到按鈕的節點,就可以控制按鈕的隱藏或顯示了!


    <script type="text/javascript">
        function doSubmit() {
            var button = document.getElementById("button");

            button.disabled = disabled;

            return true;
        }
    </script>
  • 我們再來看一下效果

  • 在處理表單的Servlet中刷新後退再提交這兩種方式不能只靠客戶端來限制了。也就是說javaScript代碼無法阻止這兩種情況的發生。
  • 於是乎,我們就想得用其他辦法來阻止表單數據重覆提交了。我們現在學了Session,Session可以用來標識一個用戶是否登陸了。Session的原理也說了:不同的用戶瀏覽器會擁有不同的Session。而request和ServletContext為什麼就不行呢?request的域對象只能是一次http請求提交表單數據的時候request域對象的數據取不出來。ServletContext代表整個web應用,如果有幾個用戶瀏覽器同時訪問ServletContext域對象的數據會被多次覆蓋掉,也就是說域對象的數據就毫無意義了。
  • 可能到這裡,我們會想到:在提交數據的時候,存進Session域對象的數據,在處理提交數據的Servlet中判斷Session域對象數據????。究竟判斷Session什麼?判斷Session域對象的數據不為null?沒用呀,既然已經提交過來了,那肯定不為null。
  • 此時,我們就想到了,在表單中還有一個隱藏域,可以通過隱藏域把數據交給伺服器
    • 判斷Session域對象的數據和jsp隱藏域提交的數據是否對應
    • 判斷隱藏域的數據是否為空【如果為空,就是直接訪問表單處理頁面的Servlet
    • 判斷Session的數據是否為空【servlet判斷完是否重覆提交,最好能立馬移除Session的數據,不然還沒有移除的時候,客戶端那邊兒的請求又來了,就又能匹配了,產生了重覆提交。如果Session域對象數據為空,證明已經提交過數據了!
  • 我們向Session域對象的存入數據究竟是什麼呢?簡單的一個數字?好像也行啊。因為只要Session域對象的數據和jsp隱藏域帶過去的數據對得上號就行了呀,反正在Servlet上判斷完是否重覆提交,會立馬把Session的數據移除掉的。更專業的做法是:向Session域對象存入的數據是一個隨機數【Token--令牌】。

  • 生成一個獨一無二的隨機數


/*
* 產生隨機數就應該用一個對象來生成,這樣可以避免隨機數的重覆。
* 所以設計成單例
* */
public class TokenProcessor {


    private TokenProcessor() {
    }

    private final static TokenProcessor TOKEN_PROCESSOR = new TokenProcessor();

    public static TokenProcessor getInstance() {
        return TOKEN_PROCESSOR;
    }


    public static String makeToken() {


        //這個隨機生成出來的Token的長度是不確定的
        String token = String.valueOf(System.currentTimeMillis() + new Random().nextInt(99999999));

        try {
            //我們想要隨機數的長度一致,就要獲取到數據指紋
            MessageDigest messageDigest = MessageDigest.getInstance("md5");
            byte[] md5 = messageDigest.digest(token.getBytes());

            //如果我們直接 return  new String(md5)出去,得到的隨機數會亂碼。
            //因為隨機數是任意的01010101010,在轉換成字元串的時候,會查gb2312的碼表,gb2312碼表不一定支持該二進位數據,得到的就是亂碼
            
            //於是乎經過base64編碼成了明文的數據
            BASE64Encoder base64Encoder = new BASE64Encoder();
            return base64Encoder.encode(md5);

        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }

        return null;

    }

}
  • 創建Token隨機數,並跳轉到jsp頁面


        //生出隨機數
        TokenProcessor tokenProcessor = TokenProcessor.getInstance();
        String token = tokenProcessor.makeToken();

        //將隨機數存進Session中
        request.getSession().setAttribute("token", token);

        //跳轉到顯示頁面
        request.getRequestDispatcher("/login.jsp").forward(request, response);
  • jsp隱藏域獲取到Session的值


<form action="/ouzicheng/Servlet7" >

    用戶名:<input type="text" name="username">
    <input type="submit" value="提交" id="button">

    <%--使用EL表達式取出session中的Token--%>
    <input type="hidden" name="token" value="${token}" >

</form>

  • 在處理表單提交頁面中判斷: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("處理請求");

            //清除Session域對象數據
            request.getSession().removeAttribute("token");

        }else {

            System.out.println("請不要重覆提交數據!");
        }
  • 下麵我們再來看一下,已經可以解決表單重覆提交的問題了!

實現原理是非常簡單的:

  • 在session域中存儲一個token
  • 然後前臺頁面的隱藏域獲取得到這個token
  • 在第一次訪問的時候,我們就判斷seesion有沒有值,如果有就比對。對比正確後我們就處理請求,接著就把session存儲的數據給刪除了
  • 等到再次訪問的時候,我們session就沒有值了,就不受理前臺的請求了!

一次性校驗碼

  • 一次性校驗碼其實就是為了防止暴力猜測密碼
  • 在講response對象的時候,我們使用response對象輸出過驗證碼,但是沒有去驗證!
  • 驗證的原理也非常簡單:生成驗證碼後,把驗證碼的數據存進Session域對象中,判斷用戶輸入驗證碼是否和Session域對象的數據一致

  • 生成驗證碼圖片,並將驗證碼存進Session域中

        //在記憶體中生成圖片
        BufferedImage bufferedImage = new BufferedImage(80, 20, BufferedImage.TYPE_INT_RGB);

        //獲取到這張圖片
        Graphics2D graphics2D = (Graphics2D) bufferedImage.getGraphics();

        //設置背景色為白色
        graphics2D.setColor(Color.white);
        graphics2D.fillRect(0, 0, 80, 20);

        //設置圖片的字體和顏色
        graphics2D.setFont(new Font(null, Font.BOLD, 20));
        graphics2D.setColor(Color.BLUE);

        //生成隨機數
        String randomNum = makeNum();

        //往這張圖片上寫數據,橫坐標是0,縱坐標是20
        graphics2D.drawString(randomNum, 0, 20);

        //將隨機數存進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());
  • 生成隨機數的方法:


    private String makeNum() {

        Random random = new Random();

        //生成0-6位的隨機數
        int num = random.nextInt(999999);

        //驗證碼的數位全都要6位數,於是將該隨機數轉換成字元串,不夠位數就添加
        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顯示頁面

<form action="/ouzicheng/Login2Servlet">

    用戶名:<input type="text" name="username"><br>
    密碼:<input type="password" name="password"><br>
    驗證碼:<input type="text" name="randomNum">
    <img src="/ouzicheng/ImageServlet" ><br><br>

    <input type="submit" value="提交">

</form>
  • 處理提交表單數據的Servlet,判斷用戶帶過來驗證碼的數據是否和Session的數據相同。


        //獲取用戶輸入驗證碼的數據
        String client_randomNum = request.getParameter("randomNum");

        //獲取Session中的數據
        String session_randomNum = (String) request.getSession().getAttribute("randomNum");

        //判斷他倆數據是否相等,用戶是否有輸入驗證碼,Session中是否為空
        if (client_randomNum == null || session_randomNum == null || !client_randomNum.equals(session_randomNum)) {
            System.out.println("驗證碼錯誤了!!!");
            return ;
        }

        //下麵就是驗證用戶名和密碼...................
  • 顯示頁面是這樣子的

  • 我們來看一下效果!

對於校驗碼實現思路是這樣子的:

  • 使用awt語法來描寫一張驗證碼,生成隨機數保存在seesion域中,我們讓驗證碼不能緩存起來【做到驗證碼都不一樣】
  • 頁面直接訪問Servlet來獲取我們的驗證碼,於是我們驗證碼的值就會改變【同時session的值也會被改變】
  • 當用戶驗證的時候,就是session內的值的驗證了。

Session和Cookie的區別

  • 從存儲方式上比較
    • Cookie只能存儲字元串,如果要存儲非ASCII字元串還要對其編碼。
    • Session可以存儲任何類型的數據,可以把Session看成是一個容器
  • 從隱私安全上比較
    • Cookie存儲在瀏覽器中,對客戶端是可見的。信息容易泄露出去。如果使用Cookie,最好將Cookie加密
    • Session存儲在伺服器上,對客戶端是透明的。不存在敏感信息泄露問題。
  • 從有效期上比較
    • Cookie保存在硬碟中,只需要設置maxAge屬性為比較大的正整數,即使關閉瀏覽器,Cookie還是存在的
    • Session的保存在伺服器中,設置maxInactiveInterval屬性值來確定Session的有效期。並且Session依賴於名為JSESSIONID的Cookie,該Cookie預設的maxAge屬性為-1。如果關閉了瀏覽器,該Session雖然沒有從伺服器中消亡,但也就失效了。
  • 從對伺服器的負擔比較
    • Session是保存在伺服器的,每個用戶都會產生一個Session,如果是併發訪問的用戶非常多,是不能使用Session的,Session會消耗大量的記憶體。
    • Cookie是保存在客戶端的。不占用伺服器的資源。像baidu、Sina這樣的大型網站,一般都是使用Cookie來進行會話跟蹤。
  • 從瀏覽器的支持上比較
    • 如果瀏覽器禁用了Cookie,那麼Cookie是無用的了!
    • 如果瀏覽器禁用了Cookie,Session可以通過URL地址重寫來進行會話跟蹤。
  • 從跨功能變數名稱上比較
    • Cookie可以設置domain屬性來實現跨功能變數名稱
    • Session只在當前的功能變數名稱內有效,不可誇功能變數名稱

Cookie和Session共同使用

  • 如果僅僅使用Cookie或僅僅使用Session可能達不到理想的效果。這時應該嘗試一下同時使用Session和Cookie

  • 那麼,什麼時候才需要同時使用Cookie和Session呢?
  • 在上一篇博客中,我們使用了Session來進行簡單的購物,功能也的確實現了。現在有一個問題:我在購物的途中,不小心關閉了瀏覽器。當我再返回進去瀏覽器的時候,發現我購買過的商品記錄都沒了!!為什麼會沒了呢?原因也非常簡單:伺服器為Session自動維護的Cookie的maxAge屬性預設是-1的,當瀏覽器關閉掉了,該Cookie就自動消亡了。當用戶再次訪問的時候,已經不是原來的Cookie了。
  • 我們現在想的是:即使我不小心關閉了瀏覽器了,我重新進去網站,我還能找到我的購買記錄
  • 要實現該功能也十分簡單,問題其實就在:伺服器為Session自動維護的Cookie的maxAge屬性是-1,Cookie沒有保存在硬碟中。我現在要做的就是:把Cookie保存在硬碟中,即使我關閉了瀏覽器,瀏覽器再次訪問頁面的時候,可以帶上Cookie,從而伺服器識別出Session。

  • 第一種方式:只需要在處理購買頁面上創建Cookie,Cookie的值是Session的id返回給瀏覽器即可


        Cookie cookie = new Cookie("JSESSIONID",session.getId());
        cookie.setMaxAge(30*60);
        cookie.setPath("/ouzicheng/");
        response.addCookie(cookie);
  • 第二種方式: 在server.xml文件中配置,將每個用戶的Session在伺服器關閉的時候序列化到硬碟或資料庫上保存。但此方法不常用,知道即可!

  • 下麵看一下效果


如果文章有錯的地方歡迎指正,大家互相交流。習慣在微信看技術文章的同學,可以關註微信公眾號:Java3y


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 這書是原本這樣還是翻譯的問題?好多的地方大小寫都寫錯了,比如javax.servlet,<servlet></servlet>。真是坑啊。 ...
  • 引言:CSP(http://www.cspro.org/lead/application/ccf/login.jsp)是由中國電腦學會(CCF)發起的“電腦職業資格認證”考試,針對電腦軟體開發、軟體測試、信息管理等領域的專業人士進行能力認證。認證對象是從事或將要從事IT領域專業技術與技術管理人 ...
  • NetCloud——一個網易雲音樂評論抓取和分析的Python庫 ...
  • 1.文件路徑: 2.修改登錄界面名稱: 顯示如圖: 3.修改後臺界面樣式: 4.Django 後臺中文和時區配置: 5.Django修改app在Admin後臺顯示的名稱: 顯示如圖: 6.添加富文本編輯器: ...
  • 這裡是cookie的簡單應用 告訴用戶您的上次訪問時間是:xxxx-xx-xx xx:xx:xx 思路: 第一次訪問該網站時候,記錄當前訪問時間(new Date()) 把當前時間以cookie的形式寫給客戶端(response.addCookie) 第二次訪問時候,獲取客戶端攜帶的相應的cooki ...
  • 該系列教程系個人原創,並完整發佈在個人官網 "劉江的博客和教程" 所有轉載本文者,需在頂部顯著位置註明原作者及www.liujiangblog.com官網地址。 Python及Django學習QQ群:453131687 除了我們前面說過的普通類型欄位,Django還定義了一組關係類型欄位,用來表示模 ...
  • 字典生成式: 跟列表生成式一樣,字典生成式用來快速生成字典,不同的是,字典需要兩個值 #d = {key: value for (key, value) in iterable} d1 = {'x': 1, 'y': 2, 'z': 3} d2 = {k: v for (k, v) in d1.it... ...
  • 一、概述 緩存(Caching)可以存儲經常會用到的信息,這樣每次需要的時候,這些信息都是立即可用的。 常用的緩存資料庫: Redis 使用記憶體存儲(in-memory)的非關係資料庫,字元串、列表、集合、散列表、有序集合,每種數據類型都有自己的專屬命令。另外還有批量操作(bulk operatio ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...