Servlet實踐-留言版-v2

来源:http://www.cnblogs.com/aristole/archive/2017/12/12/8030381.html
-Advertisement-
Play Games

為留言板-v1添加登錄功能,使用監聽器檢測會話的變化、維護活躍會話列表 ...


功能介紹:

  為留言板-v1添加登錄功能

 

如何實現登錄功能:

  1.在doGet()中檢查會話中是否包含了username特性,若包含了,則表示已經登錄,將重定向到留言板列表頁面。若不存在,則設置請求特性loginFailed為false(也就是說不是因為登錄不匹配而導致登錄失敗),將請求轉發到登錄頁面。

  2.當用戶以post的方式提交登錄表單時,在doPost()方法中將用戶提交的登錄信息與資料庫中的登錄信息比較,若一致,就重定向到留言板列表頁面;若不一致,則設置請求特性loginFailed為true,將請求轉發到登錄頁面

 

如何實現註銷功能:

  獲取HttpSession對象session,若包含請求參數logout,則調用session.invalidate()使當前會話無效,並重定向至登錄頁面。

 

使用監聽器檢測會話的變化:

  使用HttpSessionListener和HttpSessionIdListener監聽器,它們會捕獲會話事件。

  使用註解、編程或者部署描述符中聲明等方式註冊監聽器

  重寫相應的方法:

    1.當會話創建時,將調用sessionCreated(HttpSessionEvent e)方法

    2.當會話無效時,將調用sessionDestoryed(HttpSessionEvent e)方法

    3.當使用請求的changeSessionId()方法改變會話ID時將調用sessionIdChanged()方法

 

維護活躍會話列表:

  在SessionRegistry類中,維護了一個靜態的Map(以會話ID為鍵,以對應的會話對象為值),當創建會話、銷毀會話、更新會話ID時,對應地在這個map中添加新會話對象、移除對應會話對象、更新會話對象。

 

loginServlet.java

package cn.example;


import java.io.IOException;
import java.util.Hashtable;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

@WebServlet(
        name ="loginServlet",
        urlPatterns = "/login"
)
public class LoginServlet extends HttpServlet{
    // 創建一個用戶資料庫
    private static final Map<String, String> userDatabase = new Hashtable<String, String>();
    
    static{
        userDatabase.put("Nicholas", "password");
        userDatabase.put("Sarah", "drowssap");
        userDatabase.put("Mike", "wordpass");
        userDatabase.put("John", "green");
    }
    
    // 顯示登錄界面
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {    
        HttpSession session = request.getSession();
        // 添加註銷功能
        if(request.getParameter("logout") != null){
            session.invalidate();
            response.sendRedirect("login");
            return;
        }else if(session.getAttribute("username") != null){
            // 檢測用戶是否已經登錄(username特性是否存在),若已經登錄,就他們重定向至票據頁面
            response.sendRedirect("tickets");
            return;
        }
        
        // 未登錄,將請求特性中的loginFailed設置為false,將請求轉發到登錄界面
        request.setAttribute("loginFailed", false);
        request.getRequestDispatcher("/WEB-INF/jsp/view/login.jsp").forward(request, response);
    }
    
    // 處理登錄信息
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 檢測用戶是否已經登錄(username特性是否存在),若已經登錄,就他們重定向至票據頁面
        HttpSession session = request.getSession();
        if(session.getAttribute("username") != null){
            response.sendRedirect("tickets");
            return;
        }
        
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        
        // 登錄失敗,設置請求特性loginFailed為true
        if(username == null || password == null || 
                !LoginServlet.userDatabase.containsKey(username) ||
                !password.equals(LoginServlet.userDatabase.get(username))){
            request.setAttribute("loginFailed", true);
            request.getRequestDispatcher("/WEB-INF/jsp/view/login.jsp").forward(request, response);
        }else{
            // 登錄成功,把用戶名存放在session中
            session.setAttribute("username", username);
            request.changeSessionId();
            response.sendRedirect("tickets");
        }
    }
}

SessionListener.java

package cn.example;

import java.text.SimpleDateFormat;
import java.util.Date;

import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionIdListener;
import javax.servlet.http.HttpSessionListener;
@WebListener
public class SessionListener implements HttpSessionListener, HttpSessionIdListener{

    private SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    
    @Override
    public void sessionIdChanged(HttpSessionEvent e, String oldSessionId) {
        SessionRegistry.updateSessionId(e.getSession(), oldSessionId);
        System.out.println(this.date() + ": Session ID " + oldSessionId + " changed to " + e.getSession().getId());
    }

    @Override
    public void sessionCreated(HttpSessionEvent e) {
        SessionRegistry.addSession(e.getSession());
        System.out.println(this.date() + ": Session " + e.getSession().getId() + " created." );
    }

    private String date() {
        return this.formatter.format(new Date());
    }

    @Override
    public void sessionDestroyed(HttpSessionEvent e) {
        SessionRegistry.removeSession(e.getSession());
        System.out.println(this.date() + ": Session " + e.getSession().getId() + " destoryed.");
    }
    
}

SessionRegistry.java

package cn.example;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpSession;

/*
 * 一個註冊表,用來保存活躍會話的引用
 */
public final class SessionRegistry {
    private static final Map<String, HttpSession> SESSIONS = new Hashtable<String, HttpSession>();
    
    public static void addSession(HttpSession session){
        SESSIONS.put(session.getId(), session);
    }
    
    public static void updateSessionId(HttpSession session, String oldSessionId){
        synchronized (SESSIONS) {
            SESSIONS.remove(oldSessionId);
            addSession(session);
        }
    }
    
    public static void removeSession(HttpSession session){
        SESSIONS.remove(session.getId());
    }
    
    public static List<HttpSession> getAllSession(){
        return new ArrayList<HttpSession>(SESSIONS.values());
    }
    
    public static int getNumberOfSessions(){
        return SESSIONS.size();
    }
    
    private SessionRegistry(){}
}

SessionListSertvlet.java

package cn.example;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/*
 * 顯示會話
 */
@WebServlet(
        name = "sessionListServlet",
        urlPatterns = "/sessions"
)
public class SessionListServlet extends HttpServlet{
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 若用戶沒有登錄,就重定向到登錄界面
        if(request.getSession().getAttribute("username") == null){
            response.sendRedirect("login");
            return;
        }
        
        request.setAttribute("numberOfSessions", SessionRegistry.getNumberOfSessions());
        request.setAttribute("sessionList", SessionRegistry.getAllSession());
        
        
        request.getRequestDispatcher("/WEB-INF/jsp/view/sessions.jsp").forward(request, response);
    }
}

login.jsp

<!DOCTYPE html>
<html>
    <head>
        <title>留言板</title>
    </head>
    <body>
        <h2>Login</h2>
        你需要登錄到留言板<br/> <br/>
        <%
            if((Boolean)request.getAttribute("loginFailed")){
                %>
            <b>用戶名或密碼錯誤</b><br/><br/>
                <%
            }
        %>
        <form method="post" action="<c:url value="/login"/> ">
            用戶名:<br>
            <input type="text" name="username"/> <br/><br/>
            密碼:<br/>
            <input type="password" name="password" /> <br/></br/>
            <input type="submit" value="Log In"/>
        </form>
    </body>
</html>

session.jsp

<%@ page import="java.util.List" %>
<%!
    private static String toString(long timeInterval){
        if(timeInterval < 1_000)
            return "less than one second";
        if(timeInterval < 60_000)
            return (timeInterval / 1_000) + " seconds";
        return "about " + (timeInterval / 60_000) + " minutes";
    }
%>

<%
    int numberOfSessions = (Integer) request.getAttribute("numberOfSessions");
    List<HttpSession> sessions = (List<HttpSession>) request.getAttribute("sessionList");
%>

<!DOCTYPE html>
<html>
    <head>
        <title>留言板/title>
    </head>
    <body>
        <a href="<c:url value="/login?logout"/>">Logout</a>
        <h2>Sessions</h2>
        There are a total of <%= numberOfSessions %> active sessions in this application.<br/> <br/>
        <%
            long timestamp = System.currentTimeMillis();
            for(HttpSession aSession : sessions){
                out.print(aSession.getId() + " - " + aSession.getAttribute("username"));
                if(aSession.getId().equals(session.getId()))
                    out.print(" (you)");
                out.print("- last active " + toString(timestamp - aSession.getLastAccessedTime()));
                out.print(" ago<br/>");
            }
        %>
    </body>
</html

listTickets.jsp:

<%@ page session="false" import="java.util.Map" %>
<%
    @SuppressWarnings("unchecked")
    Map<Integer,Ticket> ticketDatabase = (Map<Integer, Ticket>)request.getAttribute("ticketDatabase");
%>

<!DOCTYPE html>
<html>
    <head>
        <title>留言板</title>
    </head>
    <body>
        <h2>留言板</h2>
        <a href="<c:url value="/login?logout"/>">Logout</a><br/>
        <a href="
            <c:url value="/tickets">
                <c:param name="action" value="create"/>            
            </c:url>
        ">創建留言</a><br/><br/>
        <%
            if(ticketDatabase.size() == 0){
                %><i>留言板中沒有留言。</i><%
            }
            else{
                for(int id : ticketDatabase.keySet()){
                    String idString = Integer.toString(id);
                    Ticket ticket = ticketDatabase.get(id);
                    %>留言 #<%= idString %> : <a href="
                        <c:url value="/tickets">
                            <c:param name="action" value="view"/>
                            <c:param name="ticketId" value="<%= idString %>"/>
                        </c:url>
                    "><%=ticket.getSubject() %></a>(用戶: <%= ticket.getCustomerName() %>) <br/>
                     <%
                }
            }
        %>
    </body>
</html>

ticketForm.jsp

<%@ page session="false" %>
<!DOCTYPE html>
<html>
    <head>
        <title>留言板</title>
    </head>
    <body>
        <a href="<c:url value="/login?logout"/>">Logout</a><br/>
        <h2>創建留言</h2>
        <form method="post" action="tickets" enctype="multipart/form-data">
            <input type="hidden" name="action" value="create"/>
            主題:<br/>
            <input type="text" name="subject"><br/><br/>
            內容:<br/>
            <textarea name="body" rows="5" cols="30"></textarea><br/><br/>
            <b>附件:</b><br/>
            <input type="file" name="file1" /><br/><br/>
            <input type="submit" value="提交"/>
        </form>
    </body>
</html>

viewTicket.jsp

<%
    String ticketId = (String) request.getAttribute("ticketId");
    Ticket ticket = (Ticket) request.getAttribute("ticket");
%>
<!DOCTYPE html>
<html>
    <head>
        <title>留言版</title>
    </head>
    <body>
        <a href="<c:url value="/login?logout"/>">Logout</a><br/>
        <h2>留言 #<%=ticketId %>: <%= ticket.getSubject() %></h2>
        <i>用戶 - <%=ticket.getCustomerName() %></i> <br/><br/>
        <i>內容:</i><br/>
        <%= ticket.getBody() %> <br/><br/>
        <%
            if(ticket.getNumberOfAttachments() > 0){
            %>附件:<%
                int i = 0;
                for(Attachment a:ticket.getAttachments()){
                    if(i++ > 0)
                        out.print(", ");
                    %>
                    <a href="
                        <c:url value="/tickets">
                            <c:param name="action" value="download"/>\
                            <c:param name="ticketId" value="<%= ticketId %>"/>
                            <c:param name="attachment" value="<%=a.getName() %>"/>
                        </c:url>
                        "><%=a.getName() %> </a><%
                }    
            }
        %><br/>
        <a href="<c:url value="/tickets"/>">返回留言板主頁</a>
    </body>
</html>

 


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

-Advertisement-
Play Games
更多相關文章
  • Web前端技術由 html、css 和 javascript 三大部分構成,是一個龐大而複雜的技術體系,其複雜程度不低於任何一門後端語言。而我們在學習它的時候往往是先從某一個點切入,然後不斷地接觸和學習新的知識點,因此對於初學者很難理清楚整個體系的脈絡結構。本文將對Web前端知識體系進行簡單的梳理, ...
  • > 關於譯者:這是一個流淌著滬江血液的純粹工程:認真,是 HTML 最堅實的梁柱;分享,是 CSS 里最閃耀的一瞥;總結,是 JavaScript 中最嚴謹的邏輯。經過捶打磨練,成就了本書的中文版。本書包含了函數式編程之精髓,希望可以幫助大家在學習函數式編程的道路上走的更順暢。比心。 本書主要探... ...
  • 1、set ES6 提供了新的數據結構 Set。它類似於數組,但是成員的值都是唯一的,沒有重覆的值。 使用add方法向 Set 結構加入成員。 2、size 確定set結構中有幾個元素。 3、add/delete/has/clear ...
  • 目錄 前言 JavaScript加減乘除運算 decimal.js加減乘除運算 前言 開發過程中免不了有浮點運算,JavaScript浮點運算的精度問題會帶來一些困擾 JavaScript 只有一種數字類型 ( Number ) JavaScript採用 IEEE 754 標準雙精度浮點(64),6 ...
  • 一、JavaScript簡介 JavaScript是一種解釋執行的腳本語言,是一種動態類型、弱類型、基於原型的語言,內置支持類型,它遵循ECMAScript標準。它的解釋器被稱為JavaScript引擎,為瀏覽器的一部分,廣泛用於客戶端的腳本語言,主要用來給HTML增加動態功能。 幾乎所有主流的語言 ...
  • 1單例模式2策略模式3代理模式4觀察者和發佈訂閱模式5命令模式6享元模式7責任鏈模式8裝飾者模式9狀態模式 ...
  • 各位朋友,本次LZ分享的是狀態模式,在這之前,懇請LZ解釋一下,由於最近公司事情多,比較忙,所以導致更新速度稍微慢了些(哦,往後LZ會越來越忙=。=)。 狀態模式,又稱狀態對象模式(Pattern of Objects for States),狀態模式是對象的行為模式。 狀態模式允許一個對象在其內部 ...
  • 定義: 為其他對象提供一種代理以控制對這個對象的訪問。在某些情況下,一個對象不適合或者不能直接引用另一個對象,而代理對象可以在客戶端和目標對象之間起到中介的作用 角色: 1, 抽象角色:聲明真實對象和代理對象的共同介面。 2, 代理角色:代理對象角色內部含有對真實對象的引用,從而可以操作真實對象,同 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...