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

JavaWeb 登出後瀏覽器後退/刷新重複登錄問題:Token 驗證機制徹底解決

     編輯:Java 相關知識

問題場景還原

用戶登出 → 瀏覽器點擊「後退」或「刷新」 → 表單重新提交 → 服務端復用舊 Session 或創建新 Session 卻未驗證登出狀態 → 用戶「幽靈登錄」成功,嚴重安全隱患。

根因分析

  1. 登出僅銷毀舊 HttpSession (session.invalidate())
  2. 瀏覽器後退/刷新會重發上一次 POST 請求(含舊表單數據)
  3. 容器為新請求創建新 Session,無登出標記,驗證邏輯誤以為合法登錄

解決方案:同步 Token 模式(CSRF Token 變體)

1. 登錄頁生成 Token 存入 Session + 隱藏域

<%-- login.jsp --%>
<%
    String token = String.valueOf(new Random().nextLong());
    session.setAttribute("token", token);
%>
<form action="LoginServlet" method="post">
    <input type="text" name="username"/>
    <input type="password" name="password"/>
    <input type="hidden" name="token" value="${sessionScope.token}"/>
    <input type="submit" value="登錄"/>
</form>

2. 登錄 Servlet 驗證 Token 一致性

@WebServlet("/LoginServlet")
public class LoginServlet extends HttpServlet {
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        HttpSession session = req.getSession(false); // 不自動創建
        String sessionToken = (session != null) ? (String) session.getAttribute("token") : null;
        String formToken = req.getParameter("token");
        
        // 關鍵判斷:表單 Token 與 Session Token 不匹配 → 登出後重發
        if (sessionToken == null || !sessionToken.equals(formToken)) {
            req.setAttribute("msg", "您已登出,請重新登錄");
            req.getRequestDispatcher("/message.jsp").forward(req, resp);
            return;
        }
        
        // 正常登錄邏輯...
        // 登錄成功後:移除/更新 Token,防止同一表單重複提交
        session.removeAttribute("token");
    }
}

3. 登出 Servlet 徹底銷毀

@WebServlet("/LogoutServlet")
public class LogoutServlet extends HttpServlet {
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        HttpSession session = req.getSession(false);
        if (session != null) session.invalidate();
        resp.sendRedirect(req.getContextPath() + "/login.jsp");
    }
}

為什麼這能行?

階段Session Token表單 Token匹配結果
首次登錄頁T1T1 (隱藏域)✅ 匹配 → 登錄成功,Session 移除 Token
用戶登出Session 銷毀,T1 消失瀏覽器緩存頁面仍為 T1—
後退/刷新重發新 Session,無 Token (null)舊表單攜帶 T1❌ null != T1 → 攔截,提示重新登錄

進階加固建議

  1. Token 存入數據庫/Redis:分佈式環境下 Session 不可靠,用 userId:token 存 Redis,TTL 30 分鐘
  2. 雙 Token 機制:登錄頁 Token + 登錄動作 Token,防止同一頁面多次提交
  3. 前端攔截:登出後 history.pushState(null, '', '/login') 替換歷史記錄,配合 window.onpageshow 檢測 persisted 強制跳轉
  4. SameSite Cookie:設置 Set-Cookie: JSESSIONID=xxx; SameSite=Strict 防 CSRF(現代瀏覽器支持)

常見誤區

  • ❌ 僅用 JS 禁用後退按鈕(繞過極簡單,非服務端防禦)
  • ❌ 登出後不銷毀 Session,只清屬性(Session ID 仍有效,可被重用)
  • ❌ 依賴 Cache-Control: no-store 防止緩存(瀏覽器對 POST 重發不受此影響)

同步 Token 模式是經典、輕量、無狀態的服務端防禦,配合現代前端歷史管理,可徹底根治「登出後後退重複登錄」安全漏洞。

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