2

I'm trying to get my Spring MVC app to play nice with Spring @Secured annotations and AspectJ auto-proxying but it doesn't seem to be proxying or recognising my @Secured annotations. I have a controller like this:

@Controller
@RequestMapping("/")
public class ApplicationController {

    private ApplicationFactory applicationFactory;

    @Inject
    public ApplicationController(ApplicationFactory applicationFactory) {
        super();
        this.applicationFactory = applicationFactory;
    }

    @Secured("ROLE_USER")
    @ResponseBody
    @RequestMapping(method = GET)
    public Application getApplicationInfo() {
        return applicationFactory.buildApplication(this);
    }

}

And a spring security XML that looks something like this:

Code:

  <security:global-method-security secured-annotations="enabled" mode="aspectj" proxy-target-class="true" />

  <security:http auto-config="true" use-expressions="true">
    <security:http-basic/>
  </security:http>

The above is being loaded by a no-xml Spring @Configuration component like this:

@Configuration
@ComponentScan(basePackages = {"com.example"})
@EnableWebMvc
@ImportResource("classpath:security.xml")
public class ApplicationConfiguration extends WebMvcConfigurerAdapter {

}

Which in turn is loaded using a Servlet 3.0 WebApplicationInitializer:

public class SpringMvcInitializer implements WebApplicationInitializer {

    private final AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();

    public void onStartup(ServletContext servletContext) throws ServletException {
        context.register(ApplicationConfiguration.class);

        servletContext.addListener(new ContextLoaderListener(context));
        servletContext.addListener(new Log4jConfigListener());

        final DelegatingFilterProxy proxy = new DelegatingFilterProxy("springSecurityFilterChain", context);
        FilterRegistration.Dynamic filter = servletContext.addFilter("securityFilter", proxy);
        filter.addMappingForUrlPatterns(EnumSet.of(REQUEST), false, "/*");

        final DispatcherServlet servlet = new DispatcherServlet(context);
        ServletRegistration.Dynamic dispatcher = servletContext.addServlet("dispatcher", servlet);
        dispatcher.setLoadOnStartup(1);
        dispatcher.addMapping("/*");
    }

}

However, Spring Security isn't detecting the annotation and I'm still able the secured endpoint above without being authorised. According to the Spring Security FAQ, this maybe because the <global-method-security> element is being loaded in the wrong application context, but I don't know how to ensure this using the above no-xml Spring configuration.

Am I missing something? I tried adding the @EnableAspectJAutoProxy(proxyTargetClass = true) to my application configuration but that didn't help either. Is there anyway to have run time weaving or will I have to use compile time weaving to enable annotation-based security for my application?

Ricardo Gladwell
  • 3,770
  • 4
  • 38
  • 59

2 Answers2

6

When using AOP with Spring you can choose between two implementations of AOP:

  • Spring AOP implementation doesn't require weaving, but it's only applicable to beans managed by Spring and have some limitations

  • AspectJ AOP implementation can work for all objects and doesn't have limitations of Spring AOP, but requires compile-time or load-time weaving

mode="aspectj" tells Spring to use AspectJ for AOP implementation, therefore security aspect won't work without weaving in your case.

The term "AspectJ auto-proxying" has nothing to do with using AspectJ as AOP implementation - it's a feature that allows you to use AspectJ API (rather than implementation) with Spring AOP.

So, in your case you can use Spring AOP implementation, because controller is a Spring bean, therefore you should remove mode="aspectj". Also note that your controller should have no-arguments constructor - it's one of the limitations of Spring AOP.

axtavt
  • 239,438
  • 41
  • 511
  • 482
4

To expand a bit on @axtavt's answer, the mode="aspectj" option in global-method-security specifically requires that your code has been woven with the AnnotationSecurityAspect from the spring-security-aspects module.

There's some sample code which demonstrates it in use. It just consists of a secured bean and some Junit test, but the code is compiled with the AspectJ compiler. The application context also shows how simple it is compared with what was required before namespace support was added (the commented out beans).

Shaun the Sheep
  • 22,353
  • 1
  • 72
  • 100