在 Struts 1.x 中,DispatchAction 及其子類 MappingDispatchAction 通過反射根據參數動態調用具體業務方法(如 addUI()、delete())。然而,Spring AOP 基於動態代理,**無法攔截類內部經反射調用的私有/非公共方法**,導致掛在具體業務方法上的權限切面(@Privilege)形同虛設。
DispatchAction 的 Action 中 **Override execute()**,調用 super.execute() 保持原有反射分發邏輯。execute(org.apache.struts.action.ActionForward ...) 簽名,確保能攔截到。ActionMapping 獲取目標方法名,載入 Action 類,讀取目標方法上的 @Privilege 註解。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);
}
}
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");
}
}
@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; }
}
@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();
}
}
execution(org.apache.struts.action.ActionForward com.zjh..*.*(..))execute() 後,解析 ActionMapping.parameter 或請求參數確定真實業務方法名,再讀取其註解。execute();生產環境建議採用 DelegatingRequestProcessor 覆寫 processActionPerform 方案(更徹底、無侵入)。作者註: 本文純屬 AOP 學習探索,生產級權限控制請參考「巴巴運動網」方案或升級至 Struts 2 / Spring MVC 架構。