建立一個基於 JSP/Servlet 的 MVC 登入系統,需包含以下核心功能:
HttpSessionListener 監聽 Session 建立/銷毀,即時統計線上人數Filter 進行權限控管:已登入放行、未登入導向登入頁src/
├── controller/
│ ├── LoginServlet.java
│ ├── LogoutServlet.java
│ └── MainServlet.java
├── filter/
│ └── AuthFilter.java
├── listener/
│ └── OnlineCounterListener.java
├── model/
│ └── User.java
└── util/
└── UserStore.java // 以 Map 模擬使用者資料
web/
├── login.jsp
├── main.jsp
└── error.jsp
public class UserStore {
private static final Map USERS = new ConcurrentHashMap<>();
static {
USERS.put("admin", new User("admin", "123456", "管理員"));
USERS.put("user1", new User("user1", "111111", "測試用戶"));
}
public static User validate(String username, String password) {
User u = USERS.get(username);
return (u != null && u.getPassword().equals(password)) ? u : null;
}
}
@WebListener
public class OnlineCounterListener implements HttpSessionListener {
private static final AtomicInteger onlineCount = new AtomicInteger(0);
@Override
public void sessionCreated(HttpSessionEvent se) {
onlineCount.incrementAndGet();
se.getSession().getServletContext().setAttribute("onlineCount", onlineCount.get());
}
@Override
public void sessionDestroyed(HttpSessionEvent se) {
onlineCount.decrementAndGet();
se.getSession().getServletContext().setAttribute("onlineCount", onlineCount.get());
}
public static int getOnlineCount() { return onlineCount.get(); }
}
@WebFilter("/*")
public class AuthFilter implements Filter {
private static final Set EXCLUDE_PATHS = Set.of("/login.jsp", "/login", "/logout", "/css/", "/js/", "/error.jsp");
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
String path = request.getRequestURI().substring(request.getContextPath().length());
// 靜態資源與排除路徑直接放行
if (EXCLUDE_PATHS.stream().anyMatch(path::startsWith)) {
chain.doFilter(req, res);
return;
}
HttpSession session = request.getSession(false);
if (session != null && session.getAttribute("user") != null) {
chain.doFilter(req, res); // 已登入
} else {
response.sendRedirect(request.getContextPath() + "/login.jsp"); // 未登入導向登入頁
}
}
}
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String username = req.getParameter("username");
String password = req.getParameter("password");
User user = UserStore.validate(username, password);
if (user != null) {
HttpSession session = req.getSession();
// 防止重複登入:若舊 session 存在則使其失效 (可選)
session.setAttribute("user", user);
resp.sendRedirect(req.getContextPath() + "/main");
} else {
req.setAttribute("error", "帳號或密碼錯誤");
req.getRequestDispatcher("/login.jsp").forward(req, resp);
}
}
}
<%@ page contentType="text/html;charset=UTF-8" %>
<html>
<body>
<h1>歡迎, ${sessionScope.user.username} (<span style="color:red">${sessionScope.user.realName}</span>)</h1>
<p>目前線上人數: ${applicationScope.onlineCount}</p>
<a href="${pageContext.request.contextPath}/logout">登出</a>
</body>
</html>
在 HttpSessionListener 中維護 Map,當新 Session 建立時檢查該帳號是否已有對應 Session,若有則手動 invalidate() 舊 Session。
本範例展示了純 Servlet/JSP 實作 MVC 登入系統的完整架構:Listener 負責全域狀態統計、Filter 集中處理認證授權、Servlet 處理業務流程、JSP 僅負責視圖渲染。不依賴資料庫、框架,適合教學與輕量級專案快速原型開發。