CAS (Central Authentication Service) 3.x+ 採用 Spring Web Flow 管理登錄流程,依賴 Servlet Session 存儲流程狀態(execution、_eventId 等)。集群環境下,必須解決 票據共享 與 Session 共享 兩大核心問題。本文記錄基於 tomcat-redis-session-manager 實現 Session 共享的完整配置與「Session 不更新」疑難雜症的根因分析與解決方案。
tomcat-redis-session-manager-1.1.jarjedis-2.1.0.jarcommons-pool-1.5.5.jar若版本不匹配,請至 GitHub Releases 下載對應版本。
<Context>
<Valve className="com.orangefunction.tomcat.redissessions.RedisSessionHandlerValve" />
<Manager className="com.orangefunction.tomcat.redissessions.RedisSessionManager"
host="127.0.0.1"
port="6379"
database="0"
maxInactiveInterval="1800" /> <!-- 30 分鐘,單位秒 -->
</Context>
重啟 Tomcat 後,Session 即自動序列化存入 Redis(Key 為 JSESSIONID)。
用戶點擊「登錄」按鈕,頁面僅刷新,無任何表單驗證動作,execution 參數始終為無效值(如 e2s1)。
execution,標識當前流程狀態。提交時帶回該值,Web Flow 從 Session 中取出對應 FlowExecution 繼續流程。execution = "e" + conversationId + "s" + subFlowId。conversationId 來源於 Session 屬性 webflowConversationContainer 中的自增序列。setAttribute 新 Key 或替換 Value 物件)時標記「髒數據」並寫回 Redis。直接修改物件內部字段(如 container.conversationIdSequence++)不會觸發髒標記。webflowConversationContainer 內部狀態 → Session 物件引用未變 → Manager 認定無變化 → 不同步到 Redis → 下一請求從 Redis 讀取舊 Session → 找不到新 conversationId → 判定 execution 無效 → 重新初始化流程 → 頁面刷新循環。session.getAttribute("webflowConversationContainer") → 內存中屬性確實已更新。GET JSESSIONID → 序列化數據未變化。參考《Session 同步時自定義類對象屬性保存不上的解決方法》,擴展 RedisSessionManager,重寫 isSessionDirty(),採用深度比較或序列化比較檢測物件內部變更。
在登錄請求前置 Filter 中強制寫入一個變化的屬性:
public class ForceSessionDirtyFilter implements Filter {
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) {
HttpSession session = ((HttpServletRequest)req).getSession();
session.setAttribute("_force_dirty_" + System.nanoTime(), "dirty"); // 每次請求新 Key
chain.doFilter(req, res);
}
}
在 web.xml 中映射到 /login/*。新 Key 觸發髒標記 → 整個 Session 同步到 Redis → webflowConversationContainer 內部變更隨之持久化。
maxInactiveInterval 與 CAS Ticket 過期時間一致(如 TGT 2 小時 → Session 7200 秒)。maxmemory-policy allkeys-lru 防止 OOM。cas-server-support-redis 模塊,而非自行實現。