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

破解 Struts1 與 Spring AOP 整合難題:實現方法級權限攔截

     編輯:Java 相關知識

問題背景:DispatchAction 的反射調用讓 AOP 失效

在 Struts 1.x 中,DispatchAction 及其子類 MappingDispatchAction 通過反射根據參數動態調用具體業務方法(如 addUI()、delete())。然而,Spring AOP 基於動態代理,**無法攔截類內部經反射調用的私有/非公共方法**,導致掛在具體業務方法上的權限切面(@Privilege)形同虛設。

解決思路:重寫 execute() 讓 AOP 攔截到入口,再二次反射讀取權限元數據

  1. 在所有繼承 DispatchAction 的 Action 中 **Override execute()**,調用 super.execute() 保持原有反射分發邏輯。
  2. Spring AOP 切點鎖定 execute(org.apache.struts.action.ActionForward ...) 簽名,確保能攔截到。
  3. 切面內部再次反射:從 ActionMapping 獲取目標方法名,載入 Action 類,讀取目標方法上的 @Privilege 註解。
  4. 根據註解屬性(角色類型、提示訊息)執行細粒度權限判斷,決定放行或跳轉錯誤頁。

實戰程式碼

1. Action 基類:強制覆寫 execute()

public class BaseDispatchAction extends DispatchAction {
    @Override
    public ActionForward execute(ActionMapping mapping, ActionForm form,
                                 HttpServletRequest request, HttpServletResponse response) throws Exception {
        return super.execute(mapping, form, request, response);
    }
}

2. 具體 Action 方法掛載權限註解

public class CategoryAction extends BaseDispatchAction {

    @Privilege(userType = PrivilegeType.Admin, message = "需管理員權限!")
    public ActionForward addUI(ActionMapping mapping, ActionForm form,
                               HttpServletRequest request, HttpServletResponse response) throws Exception {
        CategoryForm cf = (CategoryForm) form;
        cf.setTitle("新增分類");
        return mapping.findForward("addUI");
    }

    @Privilege(userType = PrivilegeType.LoginUser)
    public ActionForward list(ActionMapping mapping, ActionForm form,
                              HttpServletRequest request, HttpServletResponse response) throws Exception {
        // 業務邏輯
        return mapping.findForward("list");
    }
}

3. 權限註解與枚舉定義

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Privilege {
    String message() default "您沒有權限執行該操作,請登錄後重試!";
    PrivilegeType userType();
}

public enum PrivilegeType {
    LoginUser(1, "所有登錄用戶"),
    LoginUserSelf(2, "登錄用戶自己"),
    Moderator(3, "版主"),
    Admin(4, "管理員");

    private final int value;
    private final String name;
    PrivilegeType(int value, String name) { this.value = value; this.name = name; }
    public int getValue() { return value; }
    public String getName() { return name; }
}

4. Spring AOP 切面實現:核心邏輯

@Aspect
@Component
public class PrivilegeAction {

    @Around("execution(org.apache.struts.action.ActionForward com.zjh..*.*(..))")
    public Object validatePrivilege(ProceedingJoinPoint pjp) throws Throwable {
        // 1. 獲取 Struts 原始四大參數
        ActionMapping mapping = (ActionMapping) pjp.getArgs()[0];
        ActionForm form = (ActionForm) pjp.getArgs()[1];
        HttpServletRequest request = (HttpServletRequest) pjp.getArgs()[2];
        HttpServletResponse response = (HttpServletResponse) pjp.getArgs()[3];

        // 2. 反射獲取目標方法上的 @Privilege 註解
        Class<?> actionClass = Class.forName(pjp.getSignature().getDeclaringTypeName());
        Object actionInstance = actionClass.newInstance(); // 僅用於反射讀取註解

        String methodName = mapping.getParameter() == null ? "execute" 
                            : request.getParameter(mapping.getParameter());
        Method targetMethod = actionInstance.getClass().getDeclaredMethod(methodName,
                ActionMapping.class, ActionForm.class, HttpServletRequest.class, HttpServletResponse.class);
        Privilege privilege = targetMethod.getAnnotation(Privilege.class);

        // 3. 無註解直接放行
        if (privilege == null) return pjp.proceed();

        // 4. 粗粒度:必須登錄
        User user = WebUtil.getUserInSession(request);
        if (user == null) {
            request.setAttribute("message", privilege.message());
            return mapping.findForward("message");
        }

        // 5. 細粒度:角色判斷
        switch (privilege.userType().getValue()) {
            case 2: // LoginUserSelf - 略過具體業務比對邏輯
                break;
            case 3: // Moderator - 略過
                request.setAttribute("message", privilege.message());
                return mapping.findForward("message");
            case 4: // Admin
                String admin = ((SystemProperty) request.getSession().getServletContext().getAttribute("config"))
                        .getAdminUsername();
                if (!user.getUsername().equals(admin)) {
                    request.setAttribute("message", privilege.message());
                    return mapping.findForward("message");
                }
                break;
        }

        // 6. 通過檢查,放行執行原方法
        return pjp.proceed();
    }
}

關鍵點總結

  • 切點表達式 精確匹配 Struts Action 方法簽名:execution(org.apache.struts.action.ActionForward com.zjh..*.*(..))
  • 二次反射 是核心技巧:AOP 攔截到 execute() 後,解析 ActionMapping.parameter 或請求參數確定真實業務方法名,再讀取其註解。
  • 權限模型 分兩級:登錄攔截(粗)+ 角色/擁有權攔截(細),靈活擴展。
  • 局限性:需在每個 Action 手動 Override execute();生產環境建議採用 DelegatingRequestProcessor 覆寫 processActionPerform 方案(更徹底、無侵入)。

作者註: 本文純屬 AOP 學習探索,生產級權限控制請參考「巴巴運動網」方案或升級至 Struts 2 / Spring MVC 架構。

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