首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java EE 線上用戶管理系統:Filter、Listener、Servlet 實作 Session 追蹤與踢人功能

     編輯:Java 相關知識

系統功能

  • 即時顯示線上用戶數、詳細資訊 (姓名、登入時間、最後活動、IP)
  • 2. 管理員可踢出任意用戶 (強制 Session 失效) 3. 用戶可安全登出 4. 未登入攔截、字元編碼過濾

核心組件架構

組件類別職責
ListenerMySessionListenerSession 建立/銷毀時維護 Application 級線上用戶 Map
FilterCharacterFilter統一設定 UTF-8 編碼、記錄 Client IP 到 Session
FilterLoginFilter攔截 /servlet/*, /jsps/*,未登入導向 index.jsp
ServletLoginServlet處理登入,建立 User 存入 Session
ServletShowServlet查詢 Application 線上用戶 Map,轉發顯示頁
ServletKickOutServlet管理員踢人:Session.invalidate() + 移除 Map
ServletLoginOutServlet用戶主動登出:Session.invalidate()

關鍵程式碼實作

1. web.xml 部署描述符

<filter>
    <filter-name>character</filter-name>
    <filter-class>cn.hncu.filter.CharacterFilter</filter-class>
    <init-param><param-name>charset</param-name><param-value>utf-8</param-value></init-param>
</filter>
<filter>
    <filter-name>login</filter-name>
    <filter-class>cn.hncu.filter.LoginFilter</filter-class>
</filter>
<filter-mapping><filter-name>character</filter-name><url-pattern>/*</url-pattern></filter-mapping>
<filter-mapping>
    <filter-name>login</filter-name>
    <url-pattern>/servlet/*</url-pattern>
    <url-pattern>/jsps/*</url-pattern>
    <dispatcher>REQUEST</dispatcher>
    <dispatcher>FORWARD</dispatcher>
</filter-mapping>

<listener><listener-class>cn.hncu.listener.MySessionListener</listener-class></listener>

<servlet><servlet-name>LoginServlet</servlet-name><servlet-class>cn.hncu.servlet.LoginServlet</servlet-class></servlet>
<servlet><servlet-name>ShowServlet</servlet-name><servlet-class>cn.hncu.servlet.ShowServlet</servlet-class></servlet>
<servlet><servlet-name>KickOutServlet</servlet-name><servlet-class>cn.hncu.servlet.KickOutServlet</servlet-class></servlet>
<servlet><servlet-name>LoginOutServlet</servlet-name><servlet-class>cn.hncu.servlet.LoginOutServlet</servlet-class></servlet>

<servlet-mapping><servlet-name>LoginServlet</servlet-name><url-pattern>/LoginServlet</url-pattern></servlet-mapping>
<servlet-mapping><servlet-name>ShowServlet</servlet-name><url-pattern>/servlet/ShowServlet</url-pattern></servlet-mapping>
<servlet-mapping><servlet-name>KickOutServlet</servlet-name><url-pattern>/servlet/KickOutServlet</url-pattern></servlet-mapping>
<servlet-mapping><servlet-name>LoginOutServlet</servlet-name><url-pattern>/servlet/LoginOutServlet</url-pattern></servlet-mapping>

2. MySessionListener - 線上用戶註冊中心


public class MySessionListener implements HttpSessionListener {
    @Override
    public void sessionCreated(HttpSessionEvent se) {
        HttpSession session = se.getSession();
        ServletContext ctx = session.getServletContext();
        
        @SuppressWarnings("unchecked")
        Map onlines = (Map) ctx.getAttribute("onlines");
        if (onlines == null) {
            onlines = Collections.synchronizedMap(new HashMap<>()); // 線程安全
            ctx.setAttribute("onlines", onlines);
        }
        onlines.put(session.getId(), session);
    }
    
    @Override
    public void sessionDestroyed(HttpSessionEvent se) {
        HttpSession session = se.getSession();
        Map onlines = (Map) 
            session.getServletContext().getAttribute("onlines");
        if (onlines != null) onlines.remove(session.getId());
    }
}

3. CharacterFilter - 編碼與 IP 記錄


public class CharacterFilter implements Filter {
    private String charset;
    public void init(FilterConfig cfg) { charset = cfg.getInitParameter("charset"); }
    
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) 
            throws IOException, ServletException {
        req.setCharacterEncoding(charset);
        resp.setCharacterEncoding(charset);
        HttpServletRequest request = (HttpServletRequest) req;
        HttpSession session = request.getSession();
        if (session.getAttribute("ip") == null) {
            session.setAttribute("ip", request.getRemoteAddr()); // 記錄真實 IP
        }
        chain.doFilter(req, resp);
    }
    public void destroy() {}
}

4. LoginFilter - 權限攔截


public class LoginFilter implements Filter {
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) 
            throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) req;
        if (request.getSession().getAttribute("user") == null) {
            ((HttpServletResponse) resp).sendRedirect(request.getContextPath() + "/index.jsp");
            return;
        }
        chain.doFilter(req, resp);
    }
    public void init(FilterConfig cfg) {}
    public void destroy() {}
}

5. ShowServlet - 線上用戶查詢與顯示


public class ShowServlet extends HttpServlet {
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) 
            throws ServletException, IOException {
        Map onlines = (Map) 
            req.getServletContext().getAttribute("onlines");
        
        List> list = new ArrayList<>();
        if (onlines != null) {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            for (HttpSession s : onlines.values()) {
                Map m = new HashMap<>();
                m.put("id", s.getId());
                m.put("createTime", sdf.format(new Date(s.getCreationTime())));
                m.put("lastAccessedTime", sdf.format(new Date(s.getLastAccessedTime())));
                m.put("ip", s.getAttribute("ip"));
                m.put("user", s.getAttribute("user")); // User 物件或 null (訪客)
                list.add(m);
            }
        }
        req.setAttribute("onlines", list);
        req.getRequestDispatcher("/jsps/show.jsp").forward(req, resp);
    }
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) 
            throws ServletException, IOException { doPost(req, resp); }
}

6. KickOutServlet - 管理員踢人


public class KickOutServlet extends HttpServlet {
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) 
            throws ServletException, IOException {
        String id = req.getParameter("id");
        Map onlines = (Map) 
            req.getServletContext().getAttribute("onlines");
        if (onlines != null && onlines.containsKey(id)) {
            onlines.get(id).invalidate(); // 強制 Session 失效 → 觸發 Listener 移除
        }
        req.getRequestDispatcher("/servlet/ShowServlet").forward(req, resp);
    }
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) 
            throws ServletException, IOException { doPost(req, resp); }
}

7. 前端 show.jsp - 即時刷新與踢人連結

<%@ taglib prefix="c" uri="https://java.sun.com/jsp/jstl/core" %>
<meta http-equiv="refresh" content="3"> <!-- 3秒自動刷新 -->
<table>
<tr><th>姓名</th><th>上線時間</th><th>最後活動</th><th>IP</th><th>操作</th></tr>
<c:forEach items="${requestScope.onlines}" var="online">
    <tr>
        <td><c:choose>
            <c:when test="${not empty online.user}">${online.user.name}</c:when>
            <c:otherwise>訪客</c:otherwise>
        </c:choose></td>
        <td>${online.createTime}</td>
        <td>${online.lastAccessedTime}</td>
        <td>${online.ip}</td>
        <td>
            <c:if test="${online.user != sessionScope.user and not online.user.admin}">
                <a href="<c:url value="/servlet/KickOutServlet?id=${online.id}"/>">踢出</a>
            </c:if>
        </td>
    </tr>
</c:forEach>
</table>

8. User 實體與權限


public class User {
    private String name;
    private boolean admin; // true=管理員
    // Getter/Setter
}

登入時:name="admin" 設為管理員,擁有踢人權限且不可被踢。

運作流程圖解


用戶訪問
    │
    ├─ CharacterFilter → 設定編碼、記錄 IP
    │
    ├─ LoginFilter → 檢查 Session.user
    │       ├─ 無 → 重導向 index.jsp 登入
    │       └─ 有 → 放行
    │
    ├─ LoginServlet → 建立 User 存 Session
    │
    ├─ MySessionListener.sessionCreated → Application.onlines.put(sessionId, session)
    │
    ├─ ShowServlet → 讀取 onlines 映射 → 封裝 List → show.jsp 顯示
    │
    ├─ KickOutServlet → onlines.get(id).invalidate() 
    │       │
    │       └─ MySessionListener.sessionDestroyed → onlines.remove(sessionId)
    │
    └─ LoginOutServlet → session.invalidate() → 同上

關鍵設計亮點

  1. Application 域共享 Map:跨 Session 追蹤所有線上用戶。
  2. Collections.synchronizedMap:多執行緒環境下線程安全操作。
  3. Listener 解耦:Session 生命週期管理與業務邏輯分離。
  4. Filter 鏈:編碼、IP 記錄、權限攔截層層把關。
  5. 踢人機制:呼叫 invalidate() 觸發 Listener 自動清理,無需手動移除 Map。
  6. 前端自動刷新:`` 簡單實現準即時。

擴展方向

  • 引入 Redis 集中式 Session,支援叢集部署
  • 2. WebSocket 推送即時上下線通知,取代輪詢刷新 3. 加入心跳機制,精準檢測網路斷開 vs 瀏覽器關閉 4. 整合 Spring Security 替代自訂 Filter/Listener 5. 審計日誌:記錄登入/登出/被踢/踢人操作

總結

本系統展示了 Java EE 原生 API (Servlet/Filter/Listener) 構建線上用戶管理的經典模式:

  • Listener 維護全域狀態
  • Filter 處理橫切關注點 (編碼、IP、認證)
  • Servlet 處理具體業務動作
  • Session + Application 域 實現跨用戶資料共享

架構清晰、零框架依賴,是理解 Java EE Web 核心機制、進階 Spring Security/Session 共享/微服務網關的最佳教具。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved