我有一个基本的Spring MVC控制器,如下所示:
@Controller
public void MyController {
@RequestMapping("/secret")
public String show() {
return "secret.jsp";
}
}
我将有几个类似的网址,只有登录用户才能访问。由于这是一个跨领域的问题,我想使用AOP,我想通过注释来完成这项工作。换句话说,我想在每个需要保密的控制器方法上抛出@RequiresLogin
注释。
AnnotationMethodHandlerAdapter支持拦截器的概念,这似乎是正确的方法。但是,我想知道将调用哪个方法,以便我可以检查它是否为@RequiresLogin
注释。我看到有一个传入的“对象处理程序”参数,但我不知道如何将其转换为将被调用的类和方法。
想法?
答案 0 :(得分:2)
没有好方法可以在拦截器中获得方法签名。
尝试将常规AOP建议应用于您的控制器,只要使用目标类代理,Spring MVC就可以很好地使用它。
答案 1 :(得分:1)
正如axtavt正确编写的那样,如果使用proxy-target-class,Spring-AOP可以很好地与控制器配合使用。但是,如果你遵循一些(繁琐的)约定,也有可能使用JDK代理:
使用基于接口的@Controller类
与他人合作时常见的陷阱 发生了带注释的控制器类 当应用功能时 需要为其创建代理代理 控制器对象(例如
@Transactional
方法)。通常你 将介绍一个接口 控制器为了使用JDK动态 代理。 要做到这一点,你必须这样做 移动@RequestMapping
注释 到界面作为映射 机制只能“看到”界面 由代理公开。作为一个 另外,你可以选择 激活proxy-target-class =“true” 的配置 适用于的功能 控制器(在我们的交易中<tx:annotation-driven />
)中的场景。 这样做表示基于CGLIB 应该使用子类代理 而不是基于接口的JDK 代理。有关的更多信息 各种代理机制见 第7.6节,“Proxying mechanisms”。
答案 2 :(得分:1)
虽然使用spring security是最佳方法,但您可以使用Spring Aspects实现类似的功能。以下是使用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。
祝你好运!答案 3 :(得分: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
答案 4 :(得分:0)
因此,列出的这些方法很好,但它们都有局限性。 AOP的东西是一个好主意,但它的局限在于,如果我想重定向或修改响应,我需要一种方法来获取请求和响应对象。控制器方法不一定需要请求和响应,并且要求它们看起来不够优雅。我可以使用spring magic从Aspect获取请求对象,但我找不到获得响应的方法。
最终,我提出了一个中途。我使用过滤器bean来获取请求和响应对象,并将它们存储在ThreadLocal中。然后我创建了一个引用该过滤器的方面,以便它可以轻松地查看请求和响应对象。
然后我根据注释使方法包围方法,所以我甚至不需要使用代码检查注释是否存在。
这种组合方法看起来效果很好!
唯一的缺点是,我无法找到一种编写集成测试的好方法,该测试验证在对该URL有传入请求时调用该方面。删除单个注释会使我的所有测试都通过但允许未经授权的用户通过,这有点可怕。
感谢大家提出的好建议!