Aspect not executed in Spring

2019-07-18 15:18发布

问题:

I'm coding a website that will be almost fully protected by login (I'm using Spring Security for it). There are certain pages that are not protected, though (home page, login page, registration page, forgotten password page, ...) and what I'm trying to achieve is:

  • If the user is not logged in when accessing these non-secured pages, show them normally
  • If the user is already logged in, redirect to the home page (or to the page specified in the redirectTo annotation element)

Of course I want to avoid to put this in every single controller method:

if(loggedIn())
{
    // Redirect
}
else
{
    // Return the view
}

And for this reason I would like to use AOP.

I created the Annotation @NonSecured and I coded the following Aspect:

@Aspect
public class LoggedInRedirectAspect
{
    @Autowired
    private UserService userService;

    @Around("execution(@my.package.annotation.NonSecured * *(..))")
    public void redirect(ProceedingJoinPoint point) throws Throwable
    {
        System.out.println("Test");
        point.proceed();
    }
}

Example annotated method:

@Controller
@RequestMapping("/")
public class HomeController
{
    @NonSecured(redirectTo = "my-profile")
    @RequestMapping(method = RequestMethod.GET)
    public String index(Model model,
                        HttpServletRequest request) throws Exception
    {
        // Show home page
    }
}

applicationContext.xml important bits:

<context:annotation-config />
<context:component-scan base-package="my.package" />

<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true" />

<bean id="loggedInRedirectAspect" class="my.package.aspect.LoggedInRedirectAspect" />
<aop:aspectj-autoproxy proxy-target-class="true">
    <aop:include name="loggedInRedirectAspect" />
</aop:aspectj-autoproxy>

The problem is that the method redirect(...) in the aspect never gets called. Aspects in general are working fine, in fact the following method in the aspect will get called: The following advice gets called but doesn't get called for the controller methods.

@Around("execution(* *(..))")
public void redirect(ProceedingJoinPoint point) throws Throwable
{
    point.proceed();
}

Am I doing something wrong in my pointcut?

Thank you.

Update: the last snippet in this question gets called but still doesn't get called for the controller methods.

回答1:

@satoshi, I think the problem that you are having is because you are using Spring-AOP and it can create AOP proxies only for beans with interfaces - and in your case the controllers do not have an interface.

The fix could be to use compile time/load time weaving using AspectJ and to not use Spring AOP OR to have cglib jars in the classpath and to force cglib based proxy creation :

<aop:aspectj-autoproxy proxy-target-class="true"/>

Update: Compile time weaving can be done using a maven plugin, the showWeaveInfo configuration will show exactly which of the classes have been weaved:

<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>aspectj-maven-plugin</artifactId>
    <version>1.0</version>
    <dependencies>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>1.6.10</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjtools</artifactId>
            <version>1.6.10</version>
        </dependency>
    </dependencies>
    <executions>
        <execution>
            <goals>
                <goal>compile</goal>
                <goal>test-compile</goal>
            </goals>
        </execution>
    </executions>
    <configuration>
        <outxml>true</outxml>
        <verbose>true</verbose>
        <showWeaveInfo>true</showWeaveInfo>
        <aspectLibraries>
            <aspectLibrary>
                <groupId>org.springframework</groupId>
                <artifactId>spring-aspects</artifactId>
            </aspectLibrary>
        </aspectLibraries>
        <source>1.6</source>
        <target>1.6</target>
    </configuration>
</plugin>


回答2:

Usually what I would use an interceptor and not an aspect for this purpose. For example, create a RequestInitializeInterceptor which would check the security principal and redirect accordingly. Aspects is an overkill for this job. The interceptor will act as a front controller for every request to specific controllers and decide if it is allowed to transfer the request or not.

 public class RequestInitializeInterceptor extends HandlerInterceptorAdapter {

  // Obtain a suitable logger.
  private static Log logger = LogFactory
      .getLog(RequestInitializeInterceptor.class);

  /**
   * In this case intercept the request BEFORE it reaches the controller
   */
  @Override
  public boolean preHandle(HttpServletRequest request,
      HttpServletResponse response, Object handler) throws Exception {
    try {

      logger.info("Intercepting: " + request.getRequestURI());

      // Your logic to redirect accordingly
     if (userAuthenticated) {
       response.sendRedirect(URL);
       return false;
    }
      return true;
    } catch (SystemException e) {
      logger.info("request update failed");
      return false;
    }
  }
}

Hope this helps.



回答3:

What worked for me, please check following points:

  • aspectjweaver.jar is on classpath (version 1.6.8 or later)
  • Aspect class is annotated with both @Aspect and @Component
  • You enabled spring aspectJ-auto-proxy

Java config:

@Configuration
@ComponentScan("io.mc.springaspects")
@EnableAspectJAutoProxy
public class SpringConfiguration {
}

Aspect:

@Aspect
@Component
public class AnnotationAspect {
   ...
}

Maven:

<dependency>
  <groupId>org.aspectj</groupId>
  <artifactId>aspectjweaver</artifactId>
  <version>1.8.9</version>
</dependency>