首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計綜合問答

Java Web 同一賬號多標籤頁登錄:Session 共享導致登出不同步,Filter 攔截統一處理

     編輯:程式設計綜合問答

問題現象

  • 同一瀏覽器、同一賬號、兩個標籤頁 (A、B) 登錄。
  • A 頁面點擊「退出」 → `session.invalidate()` 清空 Session → 返回登錄頁。
  • B 頁面刷新 → **仍顯示登錄狀態、數據可見**,未跳轉登錄頁。

根本原因

Session 是瀏覽器級共享的** (同域名下所有標籤頁共用 `JSESSIONID` Cookie)。

  • A 執行 `invalidate()` → 服務端銷毀 Session 對象。
  • B 發起請求 → 服務端**創建新 Session** (因舊 Session 已無) → `getAttribute("user")` 返回 `null`。
  • **但 B 頁面代碼未檢查 Session 狀態**,直接渲染舊數據或靜態資源,用戶感知為「未登出」。

解決方案:統一身份驗證過濾器

1. 登錄時存入標識


// LoginServlet / Controller
session.setAttribute("LOGIN_USER", userVO); // 統一鍵名

2. 編寫 AuthenticationFilter


@WebFilter("/*") // 或配置 url-pattern 排除靜態資源、登錄頁
public class AuthenticationFilter implements Filter {
    private static final String[] EXCLUDE_PATHS = {"/login", "/css/", "/js/", "/images/", "/logout"};

    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) 
            throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) resp;
        String uri = request.getRequestURI();
        
        // 1. 排除無需驗證路徑
        if (isExcluded(uri)) {
            chain.doFilter(request, response);
            return;
        }
        
        // 2. 檢查 Session 登錄標識
        HttpSession session = request.getSession(false); // 不創建新 Session
        Object loginUser = (session != null) ? session.getAttribute("LOGIN_USER") : null;
        
        if (loginUser == null) {
            // 3. 未登錄 → 重定向登錄頁 (攜帶目標 URL 可選)
            String loginUrl = request.getContextPath() + "/login.jsp";
            response.sendRedirect(loginUrl);
            return;
        }
        
        // 4. 已登錄 → 放行
        chain.doFilter(request, response);
    }
    
    private boolean isExcluded(String uri) {
        for (String path : EXCLUDE_PATHS) {
            if (uri.startsWith(request.getContextPath() + path)) return true;
        }
        return false;
    }
}

3. 登出 Servlet 徹底清理


@WebServlet("/logout")
public class LogoutServlet extends HttpServlet {
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) 
            throws IOException {
        HttpSession session = req.getSession(false);
        if (session != null) {
            session.invalidate(); // 銷毀 Session
        }
        // 刪除 JSESSIONID Cookie (可選,防瀏覽器保留)
        Cookie cookie = new Cookie("JSESSIONID", "");
        cookie.setMaxAge(0);
        cookie.setPath(req.getContextPath());
        resp.addCookie(cookie);
        
        resp.sendRedirect(req.getContextPath() + "/login.jsp");
    }
}

進階:單點登出 (SLO) / Session 版本號

若需**所有標籤頁即時感知登出** (無需刷新),需引入:

  • WebSocket / SSE:服務端推送「登出」事件,前端收到強制跳轉。
  • 短輪詢:前端定時請求 `/check-session`,返回 401 則跳轉。
  • Session 版本號 + Redis:登錄時生成 `sessionVersion` 存 Redis,每請求校驗版本,登出時 Redis 版本 +1,所有舊 Session 失效。

最佳實踐清單

  1. **所有業務入口**統一 Filter 攔截檢查 Session。
  2. **靜態資源、登錄、登出、註冊**路徑排除 Filter。
  3. Session 鍵名**常量化** (`SessionKeys.LOGIN_USER`),避免拼寫錯誤。
  4. 登出時**同時銷毀 Session、刪除 Cookie、清除 Redis Token** (若用)。
  5. 前端 AJAX 請求攔截器處理 401/302 統一跳轉登錄頁。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved