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

Struts2 獲取與使用 Session 完整指南:ServletActionContext、#session、SessionAware 三種方式

     編輯:關於JSP

三種獲取 Session 的方式對比

方式適用場景優點缺點
ServletActionContext.getRequest().getSession()Action 方法中、攔截器、工具類最直接、功能完整 (可建立/銷毀)耦合 Servlet API、難以單元測試
實作 SessionAware 接口Action 類 (推薦)解耦 Servlet API、易測試、Struts2 自動注入僅能 Map 操作、無法直接建立/銷毀 Session
OGNL #session / <s:property value="%{#session.key}">JSP 頁面、結果視圖、標籤屬性頁面直接讀取、支援 EL 風格僅限視圖層、無法寫入複雜邏輯

方式一:ServletActionContext (Action 代碼中)

public class UserAction extends ActionSupport {
    public String login() {
        HttpServletRequest request = ServletActionContext.getRequest();
        HttpSession session = request.getSession(); // 若無則建立 (登入場景通常需要)
        
        // 存入用戶資訊
        session.setAttribute("loginUser", userService.login(username, password));
        session.setAttribute("permissions", permissionService.getPermissions(userId));
        
        return SUCCESS;
    }

    public String logout() {
        HttpSession session = ServletActionContext.getRequest().getSession(false);
        if (session != null) {
            session.invalidate(); // 徹底銷毀
        }
        return "login";
    }
}

方式二:實作 SessionAware (推薦用於 Action)

public class UserAction extends ActionSupport implements SessionAware {
    private Map sessionMap; // Struts2 自動注入 Session Map

    @Override
    public void setSession(Map session) {
        this.sessionMap = session;
    }

    public String login() {
        User user = userService.login(username, password);
        if (user != null) {
            sessionMap.put("loginUser", user);
            sessionMap.put("permissions", permissionService.getPermissions(user.getId()));
            return SUCCESS;
        }
        addFieldError("username", "帳號或密碼錯誤");
        return INPUT;
    }

    public String getUserName() {
        User u = (User) sessionMap.get("loginUser");
        return u != null ? u.getUsername() : "游客";
    }
}
  • sessionMap 實際是 org.apache.struts2.dispatcher.SessionMap,包裝了 HttpSession,支援 put/remove/get 同步到真實 Session。
  • **無法**直接呼叫 invalidate() 或 setMaxInactiveInterval(),需轉型或改用方式一。
  • 單元測試時直接傳入 new HashMap() 即可模擬。

方式三:JSP/視圖層使用 #session

<%-- 顯示用戶名 --%>
歡迎,<s:property value="%{#session.loginUser.username}" />

<%-- 迭代權限列表 --%>
<s:iterator value="%{#session.permissions}" var="perm">
    <s:property value="#perm.name" /><br/>
</s:iterator>

<%-- 判斷是否登入 --%>
<s:if test="%{#session.loginUser != null}">
    <a href="logout.action">登出</a>
</s:if>
<s:else>
    <a href="login.jsp">登入</a>
</s:else>

進階:自定義攔截器檢查登入狀態

public class AuthenticationInterceptor extends AbstractInterceptor {
    private static final String[] EXCLUDE_PATTERNS = {"/login", "/register", "/captcha", "/static/"};

    @Override
    public String intercept(ActionInvocation invocation) throws Exception {
        ActionContext ctx = invocation.getInvocationContext();
        Map session = ctx.getSession(); // 等同 SessionAware 的 Map
        
        String actionName = invocation.getProxy().getActionName();
        if (Arrays.stream(EXCLUDE_PATTERNS).anyMatch(actionName::startsWith)) {
            return invocation.invoke(); // 白名單放行
        }
        
        if (session.get("loginUser") == null) {
            // 保存原請求 URL 供登入後跳回
            String originalUrl = ServletActionContext.getRequest().getRequestURI();
            String query = ServletActionContext.getRequest().getQueryString();
            if (query != null) originalUrl += "?" + query;
            session.put("savedRequest", originalUrl);
            
            return "loginRedirect"; // struts.xml 定義全局結果
        }
        return invocation.invoke();
    }
}
<!-- struts.xml -->
<interceptors>
    <interceptor name="auth" class="com.example.interceptor.AuthenticationInterceptor"/>
    <interceptor-stack name="defaultStackWithAuth">
        <interceptor-ref name="defaultStack"/>
        <interceptor-ref name="auth"/>
    </interceptor-stack>
</interceptors>

<default-interceptor-ref name="defaultStackWithAuth"/>

<global-results>
    <result name="loginRedirect" type="redirect">/login.jsp</result>
</global-results>

Session 生命週期管理最佳實踐

  1. 登入時:request.getSession(true) 建立新 Session,或 request.changeSessionId() 防 Session Fixation。
  2. 存儲原則:僅存 必要、可序列化、體積小 的物件 (User ID、權限標識、偏好設定)。**嚴禁存放大對象、資料庫連線、非序列化物件**。
  3. 逾時設定:web.xml `30` (分鐘)。安全敏感系統建議 15-20 分鐘。
  4. 登出/超時:session.invalidate() 徹底銷毀;配合前端定時器提醒用戶即將超時。
  5. 分叢集:使用 Spring Session + Redis 或 Sticky Session,確保 Session 跨節點共享。

常見錯誤與調試

錯誤原因解決
Session 為 nullJSP 頁面 <%@ page session="false"%> 禁用了 Session移除該指令或改為 session="true"
Session 資料丟失Tomcat 重啟、Session 逾時、叢集未同步配置持久化/Redis、調長 timeout、檢查叢集
併發修改衝突多請求同時寫入 SessionMapSessionMap 內部同步,極少發生;若發生改用資料庫鎖
無法取得 ServletAPI在非 Action 類 (如 Service) 中使用 ServletActionContext傳遞 HttpSession/Map 參數,或改用 ThreadLocal 傳遞

總結

Struts2 提供三種 Session 存取途徑:Action 邏輯層首選 SessionAware (解耦、可測),需要完整 ServletAPI 時用 ServletActionContext,視圖層直接用 #session。配合自定義攔截器實現統一認證檢查,遵循「最小權限、可序列化、及時銷毀」原則管理 Session 生命週期。

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