Search code examples
spring-bootaop

Spring Boot role validation controller using aspect


I have several controller functions separated by role, and instead of doing role validation in each controller method, I found that it seems to be able to get done by using Aspect, however something isn't right in my implementation as the code in Aspect never runs

Annotation:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ForMerchantOnly {}

Aspect:

@Aspect
@Configuration
public class ForMerchantOnlyAspect {
    private static final Logger logger = LogManager.getLogger(ForMerchantOnlyAspect.class);

    @Before("@annotation(com.example.api.annotation.ForMerchantOnly) && args(request)")
    public void before(HttpServletRequest request) throws ServiceException {
        if (!(request instanceof HttpServletRequest)) {
            throw new RuntimeException("request should be HttpServletRequesttype");
        }

        String domain = request.getServerName();
        System.out.println("Aspect showing domain " + domain);
        // -- other code
    }
}

Controller

@ForMerchantOnly
@GetMapping("/list")
public ResponseEntity<ApiResp> list() {
    System.out.println("Show something");
    return ResponseEntity.ok().body();
}

I'm assuming when i call controller /list method via chrome browser, it would hit the code in ForMerchantOnlyAspect but it just went into the controller method directly. Am I missing something?


Solution

  • The Aspect was not working as it could not find a matching joinpoint . There are no controller methods that has annotation @ForMerchantOnly and has an argument of type HttpServletRequest

    From the documentation :

    args: Limits matching to join points (the execution of methods when using Spring AOP) where the arguments are instances of the given types.

    Following aspect may be used for the requirement . Scoping designator within will set the scope to advice.

    @Before("@annotation(com.example.api.annotation.ForMerchantOnly) && within(com.example.api..*)")
      public void before() {
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes())
                    .getRequest();
    
            System.out.println("Aspect showing domain " + request.getServerName());
        }
    

    Also note that an Aspect is better annotated with @Component and @Configuration be used for configurations.

    You may also have a look at Method Security of Spring security framework , which lets to secure a method with annotations.

    From the documentation

    From version 2.0 onwards Spring Security has improved support substantially for adding security to your service layer methods. It provides support for JSR-250 annotation security as well as the framework’s original @Secured annotation. From 3.0 you can also make use of new expression-based annotations. You can apply security to a single bean, using the intercept-methods element to decorate the bean declaration, or you can secure multiple beans across the entire service layer using the AspectJ style pointcuts.