src/
├── com/ncu/
│ ├── login.java // 登錄頁面 Servlet (GET)
│ ├── LoginPro.java // 登錄處理 Servlet (POST)
│ └── Welcome.java // 歡迎頁 Servlet (GET)
└── web.xml // Servlet 映射配置
login.java (GET 請求)職責:輸出 HTML 登錄表單,action 指向處理 Servlet。
package com.ncu;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class login extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setContentType("text/html;charset=UTF-8"); // 建議 UTF-8
PrintWriter out = resp.getWriter();
out.println("");
out.println("用戶登錄 ");
out.println("用戶登錄界面
");
out.println("");
}
}
改進點:使用 UTF-8 編碼;HTML 結構規範;表單 method=post 保護密碼不顯示在 URL。
LoginPro.java (POST 請求)職責:接收參數、驗證憑證、建立會話、重定向。
package com.ncu;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
public class LoginPro extends HttpServlet {
// 實際項目應從數據庫/配置讀取,此處僅演示
private static final String VALID_USER = "nicolas";
private static final String VALID_PWD = "nicolas";
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String user = req.getParameter("userName");
String pwd = req.getParameter("Pwd");
// 參數防空
if (user == null) user = "";
if (pwd == null) pwd = "";
if (VALID_USER.equals(user) && VALID_PWD.equals(pwd)) {
// 驗證成功:創建 Session,存儲用戶標識
HttpSession session = req.getSession(true);
session.setAttribute("loginUser", user);
session.setMaxInactiveInterval(30 * 60); // 30 分鐘超時
// 重定向到歡迎頁(避免表單重複提交)
resp.sendRedirect(req.getContextPath() + "/welcome");
} else {
// 驗證失敗:重定向回登錄頁,可攜帶錯誤提示
resp.sendRedirect(req.getContextPath() + "/login?error=1");
}
}
}
關鍵最佳實踐:
HttpSession 維持登錄狀態,而非 URL 重寫或隱藏字段。session.setMaxInactiveInterval() 控制超時。Welcome.java (GET 請求)職責:校驗 Session,顯示個性化歡迎或重定向登錄。
package com.ncu;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
public class Welcome extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
HttpSession session = req.getSession(false); // 不創建新 Session
String user = (session != null) ? (String) session.getAttribute("loginUser") : null;
resp.setContentType("text/html;charset=UTF-8");
PrintWriter out = resp.getWriter();
if (user != null) {
out.println("歡迎," + user + "!
");
out.println("退出登錄");
} else {
// 未登錄或 Session 失效,重定向登錄頁
resp.sendRedirect(req.getContextPath() + "/login");
}
}
}
@WebServlet("/logout")
public class Logout extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws IOException {
HttpSession session = req.getSession(false);
if (session != null) session.invalidate(); // 銷毀 Session
resp.sendRedirect(req.getContextPath() + "/login");
}
}
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0"
xmlns="https://java.sun.com/xml/ns/javaee"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://java.sun.com/xml/ns/javaee
https://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<display-name>LoginDemo</display-name>
<servlet>
<servlet-name>login</servlet-name>
<servlet-class>com.ncu.login</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>login</servlet-name>
<url-pattern>/login</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>loginpro</servlet-name>
<servlet-class>com.ncu.LoginPro</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>loginpro</servlet-name>
<url-pattern>/loginpro</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>welcome</servlet-name>
<servlet-class>com.ncu.Welcome</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>welcome</servlet-name>
<url-pattern>/welcome</url-pattern>
</servlet-mapping>
<!-- 可選:Session 超時配置(分鐘) -->
<session-config>
<session-timeout>30</session-timeout>
</session-config>
<welcome-file-list>
<welcome-file>login</welcome-file> <!-- 默認進入登錄頁 -->
</welcome-file-list>
</web-app>
避免在每個 Servlet 重複檢查 Session,編寫 LoginFilter:
@WebFilter(urlPatterns = {"/welcome", "/user/*", "/admin/*"})
public class LoginFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;
HttpSession session = req.getSession(false);
if (session != null && session.getAttribute("loginUser") != null) {
chain.doFilter(request, response); // 已登錄,放行
} else {
resp.sendRedirect(req.getContextPath() + "/login"); // 未登錄,跳轉
}
}
}
在 web.xml 或註解中註冊 Filter 即可實現全站權限控制。
| 風險 | 防範措施 |
|---|---|
| 密碼明文傳輸 | 全站強制 HTTPS (TLS 1.2+) |
| 密碼明文存儲 | BCrypt/Argon2 + Salt 哈希存儲 |
| 暴力破解 | 登錄失敗計數 + 驗證碼 + 帳號鎖定策略 |
| Session 劫持 | HttpOnly、Secure 標記;定期輪換 Session ID |
| CSRF 攻擊 | 關鍵操作驗證 CSRF Token (雙重提交 Cookie 模式) |
| SQL 注入 | 使用 PreparedStatement / ORM 參數綁定 |
本示例展示了基於 Servlet + Session 的經典登錄流程:
login (GET) → 渲染表單LoginPro (POST) → 驗證 → 建立 Session → 重定向Welcome (GET) → 校驗 Session → 顯示內容或跳轉Logout → 銷毀 Session實際項目中建議: