2011-02-03 121 views
1

我有一個基本的Spring MVC控制器,它看起來像這樣:春AnnotationMethodHandlerAdapter上和註釋讀攔截

@Controller 
public void MyController { 
    @RequestMapping("/secret") 
    public String show() { 
     return "secret.jsp"; 
    } 
} 

我將不得不只能通過登錄用戶達到幾個相似的網址。由於這是一個交叉問題,我想使用AOP,並且我想通過註釋來完成這項工作。換句話說,我想在每個需要保密的控制器方法上註釋一個@RequiresLogin註解。

AnnotationMethodHandlerAdapter支持攔截器的概念,這在表面上看起來就像正確的方式。但是,我想知道將調用哪種方法,以便我可以檢查它是否適用於我的@RequiresLogin註釋。我看到有一個傳入的「Object handler」參數,但我不確定如何將其轉換爲將被調用的Class和Method。

想法?

+0

豈不春季安全能夠處理這個,而不需要你註釋你的控制器嗎? – limc 2011-02-03 00:25:06

+0

也許,但我們的項目沒有使用Spring Security,我想修改genearl中的方法的過濾行爲,而不僅僅是授權。也許爲某些方法添加一個自定義額外頭文件等。 – 2011-02-03 00:34:24

回答

1

由於axtavt正確寫入,如果使用proxy-target-class,Spring-AOP可以很好地與控制器配合使用。但也有使用JDK代理,如果你遵循一些(乏味)公約的可能性:

基於接口的@Controller類

一個常見的錯誤與 註解控制器類時發生的工作 施加時的功能,需要 創建代理代理爲 控制器對象(例如 @Transactional方法)。通常,您將爲 控制器引入一個接口,以便使用JDK動態 代理。 爲了使這項工作必須 移動@RequestMapping註釋 到接口作爲映射 機制只能「看見」通過代理公開的接口 。作爲 選擇,你可以選擇 激活代理目標類=「真」在 爲應用於 控制器 功能配置(在我們的交易 在<tx:annotation-driven />情況下)。 這樣做指示基於CGLIB的 子類代理應使用 代替基於接口的JDK 代理。有關 各種代理機制的更多信息,請參閱 第7.6節,「Proxying mechanisms」

來源:15.3.2 Mapping requests with @RequestMapping

2

沒有好方法在攔截器中獲得方法簽名。

嘗試將常規AOP建議應用於您的控制器,只要使用目標類代理,Spring MVC就可以很好地運行它。

1

在使用彈簧安全將這裏是最佳的方法,您可以使用Spring方面實現類似的功能。以下是使用Aspect來檢查包含特定註釋的方法的示例。

@Aspect 
public class MyAspect { 
    @Around("execution(* com.test.controllers..*.**(..)) && " + 
      "within(@org.springframework.sterotype.Controller *)") 
    public Object execute(ProceedingJoinPoint joinPoint) { 
     Object target = joinPoint.getTarget(); 
     if (target != null) { 
      Signature tSig = joinPoint.getSignature(); 
      if (tSig instanceof MethodSignature) { 
       MethodSignature mSig = (MethodSignature) tSig; 
       Method method = mSig.getMethod(); 
       if (method != null && method.isAnnotationPresent(MyAnnotation.class)) { 
        // do something 
        // parameters are available from joinPoint.getArgs(); 
       } 
      } 
     } 
    } 
    // allow method invocation to continue 
    return joinPoint.proceed(); 
}

@Around建議的格式將特定於您的應用程序。在這個例子中,它檢查包com.test.controllers和所有子包中的Controller註釋的任何類。有關其他選項,請參閱http://static.springsource.org/spring/docs/3.0.x/reference/aop.html

祝你好運!

+0

是的,但是axtavt和我自己的文章中列出的限制適用(除非您使用AspectJ編譯時織入) – 2011-02-03 14:18:05

0

因此,列出的這些方法都很好,但它們都有侷限性。 AOP的東西是一個好主意,但它的侷限性在於,如果我想重定向或修改響應,我需要一種方法來獲取請求和響應對象的位置。控制器方法不一定需要這些請求和響應,並且要求它們看起來不雅觀。我可以使用Spring魔法從Aspect中獲取請求對象,但是我找不到獲取響應的方法。

最後,我想出了一條中間路線。我使用了一個過濾器bean來獲取請求和響應對象,並將它們存儲在一個ThreadLocal中。然後我創建了一個引用該過濾器的方面,以便它可以輕鬆查看請求和響應對象。

然後我做了方面環繞基於註釋的方法,所以我甚至不需要檢查註釋是否存在使用代碼。

這種組合方式看起來很完美!

唯一的缺點是我找不到一個好的方法來編寫一個集成測試,驗證在有對該URL的傳入請求時調用該方面。移除單個註釋會讓所有測試都通過但允許未經授權的用戶通過,這有點令人害怕。

謝謝大家的好建議!

1

如何使用反射ResolveHandlerMethodInterceptor。 下面的代碼是實驗和版本相關的(spring 3.0.2)。

import java.lang.reflect.Method; 

import javax.servlet.ServletContext; 
import javax.servlet.http.HttpServletRequest; 
import javax.servlet.http.HttpServletResponse; 

import org.springframework.web.context.WebApplicationContext; 
import org.springframework.web.context.support.WebApplicationContextUtils; 
import org.springframework.web.servlet.FrameworkServlet; 
import org.springframework.web.servlet.HandlerInterceptor; 
import org.springframework.web.servlet.ModelAndView; 
import org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter; 

public class ResolveHandlerMethodInterceptor implements HandlerInterceptor { 
    public final static String HANDLER_METHOD = "handlerMethod"; 
    // Here is your servlet name 
    public final static String SERVLET_NAME = "XXXXX"; 


    @Override 
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object object, ModelAndView modelAndView) 
      throws Exception { 
     Method handlerMethod = (Method) request.getAttribute(HANDLER_METHOD); 
     System.out.println("postHandle>>>" + handlerMethod); 
    } 

    @Override 
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object object, Exception exception) 
      throws Exception { 
     Method handlerMethod = (Method) request.getAttribute(HANDLER_METHOD); 
     System.out.println("afterCompletion>>>" + handlerMethod); 
    } 

    @Override 
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object object) throws Exception { 

     ServletContext servletContext = request.getSession().getServletContext(); 
     String attrName = FrameworkServlet.SERVLET_CONTEXT_PREFIX + SERVLET_NAME; 
     WebApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(servletContext, attrName); 
     AnnotationMethodHandlerAdapter adapter = context.getBean(AnnotationMethodHandlerAdapter.class);  

     Method getMethodResolverMethod = adapter.getClass().getDeclaredMethod("getMethodResolver", Object.class); 
     getMethodResolverMethod.setAccessible(true); 
     Object servletHandlerMethodResolver = getMethodResolverMethod.invoke(adapter, object); 

     Method resolveHandlerMethod = servletHandlerMethodResolver.getClass().getMethod("resolveHandlerMethod", HttpServletRequest.class); 
     resolveHandlerMethod.setAccessible(true); 
     Method handlerMethod = (Method) resolveHandlerMethod.invoke(servletHandlerMethodResolver, request); 
     request.setAttribute(HANDLER_METHOD, handlerMethod); 

     System.out.println("preHandle>>>" + handlerMethod); 

     return true; 
    } 
} 

== ==參考
http://toby.epril.com/?p=934
http://www.jarvana.com/jarvana/view/org/springframework/spring-webmvc/3.0.2.RELEASE/spring-webmvc-3.0.2.RELEASE-sources.jar!/org/springframework/web/servlet/mvc/annotation/AnnotationMethodHandlerAdapter.java?format=ok