自動登入讓用戶在瀏覽器關閉後再次訪問時,無需重複輸入帳密即可恢復登入狀態。核心機制:登入成功時將用戶識別資訊加密存儲於客戶端 Cookie,後續請求攜帶該 Cookie 時,伺服端驗證並自動重建 Session。
| 屬性 | 說明 | 自動登入設定建議 |
|---|---|---|
name/value | 鍵值對,如 autoLogin / username | 值建議加密或使用 Token,避免明文存儲敏感資訊 |
maxAge | 生存秒數,-1 為會話級,0 刪除 | 依「記住我」選項設定:1天=86400、7天=604800 |
path | Cookie 生效路徑前綴 | 設為 request.getContextPath(),全站生效 |
domain | 跨子域共享 | 需跨子域時設定(如 .example.com) |
secure | 僅 HTTPS 傳輸 | 生產環境必須啟用 |
httpOnly | 禁止 JavaScript 讀取 | 防 XSS,必須啟用 |
<c:choose>
<c:when test="${empty sessionScope.name}">
<form method="post" action="<c:url value='/LoginServlet'/>">
帳號: <input type="text" name="name"/><br/>
自動登入:
<input type="radio" name="auto" value="-1" checked> 不自動登入 <br/>
<input type="radio" name="auto" value="1"> 1 天 <br/>
<input type="radio" name="auto" value="7"> 1 週 <br/>
<input type="submit" value="登入"/>
</form>
</c:when>
<c:otherwise>
歡迎 ${name} <br/>
<a href="<c:url value='/LoginServlet'/>">登出</a>
</c:otherwise>
</c:choose>
@WebServlet("/LoginServlet")
public class LoginServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String name = request.getParameter("name");
String auto = request.getParameter("auto");
// 驗證用戶合法性(略,實際應查詢資料庫)
request.getSession().setAttribute("name", name);
if (!"-1".equals(auto)) {
int days = Integer.parseInt(auto); // 1 或 7
int seconds = 60 * 60 * 24 * days;
Cookie cookie = new Cookie("autoLogin", name); // 實際應存加密 Token
cookie.setMaxAge(seconds);
cookie.setPath(request.getContextPath());
cookie.setHttpOnly(true);
cookie.setSecure(request.isSecure()); // 僅 HTTPS 時 true
response.addCookie(cookie);
}
response.sendRedirect(request.getContextPath() + "/index.jsp");
}
}
@WebFilter(urlPatterns = "/*")
public class AutoLoginFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
// 排除登入頁與靜態資源,避免重複處理
String uri = req.getRequestURI();
if (uri.endsWith("/LoginServlet") || uri.contains("/static/")) {
chain.doFilter(request, response);
return;
}
// 檢查 Session 是否已有用戶
if (req.getSession().getAttribute("name") == null) {
Cookie[] cookies = req.getCookies();
if (cookies != null) {
for (Cookie c : cookies) {
if ("autoLogin".equals(c.getName())) {
String username = c.getValue(); // 實際應驗證 Token 有效性
// 這裡可進一步查詢資料庫驗證 Token 對應用戶是否存在
req.getSession().setAttribute("name", username);
break;
}
}
}
}
chain.doFilter(request, response);
}
}
@WebServlet("/LogoutServlet")
public class LogoutServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 1. 使 Session 失效
request.getSession().invalidate();
// 2. 刪除自動登入 Cookie
Cookie cookie = new Cookie("autoLogin", "");
cookie.setMaxAge(0);
cookie.setPath(request.getContextPath());
response.addCookie(cookie);
response.sendRedirect(request.getContextPath() + "/index.jsp");
}
}
token -> user_id, expiry 映射@WebFilter(urlPatterns = {"/jsps/*", "/views/*"})
public class LoginRequiredFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;
if (req.getSession().getAttribute("user") == null) {
req.getSession().setAttribute("msg", "請先登入");
resp.sendRedirect(req.getContextPath() + "/index.jsp");
} else {
chain.doFilter(request, response);
}
}
}
在 AutoLoginFilter 中針對登入 Servlet、靜態資源、API 端點等跳過 Cookie 解析,減少無謂運算。
自動登入 = 持久化 Cookie + Filter 攔截驗證 + Session 重建。生產環境務必採用 Token 機制、啟用 HttpOnly/Secure、實作 Token 撤銷與異地登入檢測,平衡使用者體驗與帳戶安全。