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

Struts 2.5.5 攔截器實戰:從核心配置到登錄認證攔截器完整實現

     編輯:Java 綜合教學

Struts 2.5.5 攔截器實戰指南

Struts 2.5.x 版本對核心控制器、配置結構進行了重大調整。本文基於 2.5.5 版本,系統講解攔截器配置新語法、自定義攔截器開發、攔截器棧組合,並以「登錄認證攔截器」為例提供生產級實現代碼。

版本關鍵變更 (2.3 → 2.5)

項目Struts 2.3Struts 2.5+
核心過濾器org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilterorg.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter
配置文件 DTDstruts-2.3.dtdstruts-2.5.dtd
包結構org.apache.struts2.*基本兼容,部分內部類移動
最低 JDKJDK 6JDK 7+

web.xml 核心配置 (2.5 版本語法)

<web-app xmlns="https://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="https://xmlns.jcp.org/xml/ns/javaee 
         https://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">

    <!-- Struts 2.5 核心過濾器 -->
    <filter>
        <filter-name>struts2</filter-name>
        <filter-class>org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter</filter-class>
        
        <!-- 編碼設置 -->
        <init-param>
            <param-name>struts.i18n.encoding</param-name> <!-- 注意: i18n 非 il8 -->
            <param-value>UTF-8</param-value>
        </init-param>
        
        <!-- struts.xml 路徑 (可選,默認 classpath:struts.xml) -->
        <init-param>
            <param-name>filterConfig</param-name>
            <param-value>classpath:struts.xml</param-value>
        </init-param>
    </filter>
    
    <filter-mapping>
        <filter-name>struts2</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
</web-app>

struts.xml 完整配置解析

<struts>
    <!-- 全局常量配置 -->
    <constant name="struts.i18n.encoding" value="UTF-8" />
    <constant name="struts.devMode" value="true" /> <!-- 開發模式:配置修改熱加載 -->
    <constant name="struts.configuration.xml.reload" value="true" />
    <constant name="struts.action.extension" value="action," /> <!-- 支持無後綴訪問 -->
    <constant name="struts.enable.DynamicMethodInvocation" value="false" /> <!-- 禁用動態方法調用 (!) -->
    <constant name="struts.serve.static.browserCache" value="false" /> <!-- 開發期禁用靜態資源緩存 -->
    <constant name="struts.convention.default.parent.package" value="common"/>
    
    <!-- 公共包:定義攔截器、全局結果、通用配置 -->
    <package name="common" namespace="/" extends="struts-default">
        
        <!-- 1. 註冊自定義攔截器 -->
        <interceptors>
            <interceptor name="loginInterceptor" 
                class="com.demo.action.LoginInterceptor" />
            
            <!-- 2. 組合自定義攔截器棧 (必須包含 defaultStack) -->
            <interceptor-stack name="loginStack">
                <interceptor-ref name="defaultStack" /> <!-- 繼承默認棧 (params, validation, workflow 等) -->
                <interceptor-ref name="loginInterceptor" /> <!-- 添加自定義攔截器 -->
            </interceptor-stack>
        </interceptors>
        
        <!-- 3. 設置包默認攔截器棧 -->
        <default-interceptor-ref name="loginStack" />
        
        <!-- 4. 全局結果映射 -->
        <global-results>
            <result name="noLogin">/WEB-INF/pages/sweet/404.jsp</result>
            <result name="error">/WEB-INF/pages/error.jsp</result>
        </global-results>
        
        <!-- Action 定義 (或在子包中定義) -->
        <action name="login" class="com.demo.action.LoginAction">
            <result name="success">/main.jsp</result>
            <result name="input">/login.jsp</result>
        </action>
        
    </package>
</struts>

自定義登錄攔截器實現

package com.demo.action;

import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.interceptor.Interceptor;
import org.apache.struts2.ServletActionContext;

public class LoginInterceptor implements Interceptor {
    
    @Override
    public void init() {
        // 初始化邏輯 (如加載白名單配置)
        System.out.println("🔧 LoginInterceptor 初始化");
    }
    
    @Override
    public String intercept(ActionInvocation invocation) throws Exception {
        // 1. 獲取當前 Action 實例
        Object action = invocation.getAction();
        
        // 2. 放行登錄相關 Action (避免死循環)
        if (action instanceof LoginAction) {
            return invocation.invoke();
        }
        
        // 3. 白名單放行 (可配置化)
        String actionName = invocation.getProxy().getActionName();
        if (isExcluded(actionName)) {
            return invocation.invoke();
        }
        
        // 4. 檢查 Session 登錄狀態
        Object user = ServletActionContext.getRequest().getSession().getAttribute("user");
        if (user == null) {
            System.out.println("🚫 未登錄訪問: " + actionName);
            return "noLogin"; // 對應 global-results 中的 noLogin
        }
        
        // 5. 放行
        return invocation.invoke();
    }
    
    @Override
    public void destroy() {
        // 銷毀邏輯 (如釋放資源)
        System.out.println("🗑️ LoginInterceptor 銷毀");
    }
    
    // 白名單判斷 (建議從配置文件讀取)
    private boolean isExcluded(String actionName) {
        return actionName.startsWith("static/") || 
               actionName.equals("login") || 
               actionName.equals("register") ||
               actionName.equals("captcha");
    }
}

登錄 Action 實現 (ModelDriven 模式)

package com.demo.action;

import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;
import org.apache.struts2.convention.annotation.Action;
import org.apache.struts2.convention.annotation.Result;
import org.apache.struts2.convention.annotation.Results;

@Results({
    @Result(name="success", location="/main.jsp"),
    @Result(name="input", location="/login.jsp")
})
public class LoginAction extends ActionSupport implements ModelDriven<User> {
    
    private User user = new User();
    
    @Override
    public User getModel() {
        return user; // 表單參數自動注入 user 對象
    }
    
    @Action(value = "login")
    public String execute() throws Exception {
        // 業務邏輯:驗證用戶名密碼
        if ("admin".equals(user.getUsername()) && "admin".equals(user.getPassword())) {
            // 登錄成功:存入 Session
            ServletActionContext.getRequest().getSession().setAttribute("user", user);
            System.out.println("✅ 登錄成功: " + user.getUsername());
            return SUCCESS;
        } else {
            addFieldError("username", "用戶名或密碼錯誤");
            System.out.println("❌ 登錄失敗: " + user.getUsername());
            return INPUT;
        }
    }
    
    // 登出 Action
    @Action(value = "logout", results = @Result(name="success", location="/login.jsp", type="redirect"))
    public String logout() {
        ServletActionContext.getRequest().getSession().invalidate();
        return SUCCESS;
    }
}

User 實體類

package com.demo.entity;

public class User {
    private String username;
    private String password;
    // Getter/Setter 省略
}

進階攔截器模式

1. 方法級攔截 (Dynamic Method Invocation)

<action name="user/*" class="com.demo.action.UserAction" method="{1}">
    <interceptor-ref name="loginStack">
        <!-- 排除特定方法 -->
        <param name="excludeMethods">login,register,logout</param>
    </interceptor-ref>
    <result name="noLogin">/login.jsp</result>
</action>

2. 注解式攔截器配置 (約定優於配置)

@InterceptorRefs({
    @InterceptorRef(value = "defaultStack", params = {"excludeMethods", "login,register"}),
    @InterceptorRef("loginInterceptor")
})
@ParentPackage("common")
@Namespace("/")
@Results({...})
public class UserAction extends ActionSupport { ... }

3. Session 過期處理增強

// 在 LoginInterceptor.intercept() 中增強
HttpServletRequest request = ServletActionContext.getRequest();
HttpSession session = request.getSession(false); // 不創建新 Session

if (session == null || session.getAttribute("user") == null) {
    // AJAX 請求返回 JSON
    if ("XMLHttpRequest".equals(request.getHeader("X-Requested-With"))) {
        HttpServletResponse response = ServletActionContext.getResponse();
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().write("{\"code\":401,\"msg\":\"會話過期,請重新登錄\"}");
        return null; // 重要:返回 null 表示已手寫響應
    }
    return "noLogin";
}

常見問題排查

現象原因解決
攔截器不生效未加入 defaultStack 或 default-interceptor-ref 配置錯誤檢查 interceptor-stack 包含 defaultStack、default-interceptor-ref 指向正確棧名
靜態資源 (CSS/JS) 被攔截url-pattern 為 /* 攔截所有請求1. 白名單排除 2. 配置 <constant name="struts.action.excludePattern" value="/static/.*" />
無限循環跳轉登錄頁登錄 Action 被攔截攔截器中判斷 action instanceof LoginAction 放行
Session 取值為 null請求未經過 Struts 過濾器或 Session 已失效檢查 filter-mapping url-pattern、Session 超時配置
中文參數亂碼編碼設置錯誤web.xml 和 struts.xml 同時設置 struts.i18n.encoding=UTF-8

最佳實踐總結

  1. 繼承 defaultStack:自定義棧必須包含默認棧,保證參數注入、校驗、文件上傳等核心功能
  2. 白名單機制:登錄、註冊、驗證碼、靜態資源等配置排除路徑,避免硬編碼
  3. 異步請求識別:AJAX 返回 401 JSON 而非重定向,前端統一處理跳轉
  4. Session 管理:使用 request.getSession(false) 避免創建無用 Session
  5. 攔截器順序:defaultStack 先執行參數注入,自定義攔截器後執行業務邏輯
  6. 配置外部化:白名單、結果頁面路徑等提取到屬性文件/數據庫

Struts 2.5 的攔截器機制是其核心優勢所在。掌握 註冊→組合棧→設置默認→白名單放行→業務邏輯 這套標準流程,配合註解式配置與約定優於配置原則,即可構建清晰、可維護、安全的 Web 應用安全防線。

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