1

I have a spring boot application with a custom filter:

import javax.servlet.Filter

public class MyFilter implements Filter{

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
        System.out.println("MyFilter");
    }
}

Some controllers in the application have custom annotations over their methods. All custom annotations implement the myAnnotation interface.

    @GetMapping("/")
    @MyCustomAnnotation
    @AnotherCustomAnnotation
    public ResponseEntity<String> get(){
        addSomeMetric();
        return new ResponseEntity<>("Hello world", HttpStatus.OK);
    }

How can get i get a list of all annotations defined on the URI endpoint from within the doFilter code?

user12396421
  • 175
  • 1
  • 10
  • What is your purpose? Maybe you can define aop advice operations. – Ismail Durmaz Jan 20 '21 at 21:11
  • The filter should gather all the annotations on the resource and then check if the request is allowed to proceed. (let's say it it is only open for users with a certain attribute) – user12396421 Jan 20 '21 at 21:15
  • `MvcFilter` will select the best available methods within the request. `Filter` is simpler that manages request and response. I can show an aspect-oriented solution that can manage annotations – Ismail Durmaz Jan 20 '21 at 21:26
  • A filter executes before a servlet mapping is selected for dispatch. You can't get a handle to the class because the system doesn't know which one it will use yet. – Deadron Jan 20 '21 at 21:59
  • so, if not in a filter, where is the right place to enforce the annotations? – user12396421 Jan 20 '21 at 22:37
  • Filters are called before the actual resource to play around request and response. It has no knowledge what resource will be kicked in. You can opt in alternate approach to map url with class name and method name in property file. Use detail in filter to know what class and method will be called and then use reflection api to get rest of the detail. – Satyendra Sharma Jan 20 '21 at 22:40

2 Answers2

2

I eventually used a HandlerInterceptor and not a filter.

Since I am only aiming for web requests, it seems a better and easier solution than AOP.

    @Override
    public boolean preHandle(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response, @NonNull Object handler) {
       MyAnnotation myAnnotation = getMyAnnotation(handler);

    }

    private MyAnnotation getMyAnnotation(Object handler) {
       HandlerMethod handlerMethod = (HandlerMethod) handler;
       Method method = handlerMethod.getMethod();
       return method.getAnnotation(MyAnnotation.class);
}
user12396421
  • 175
  • 1
  • 10
1

Spring AOP can be used to decide if a controller method need to be executed based on request/user.

Following code is one way of implementing the same.

@Aspect
@Component
public class AnnotationFilterAspect {
    
    @Pointcut("execution(public * package.controller..*(..))")
    public void allControllerMethods() {
        
    }
    
    @Around("allControllerMethods()")
    public Object checkAccess(ProceedingJoinPoint pjp) {
        Object retObject = null;
        Method method = getMethod(pjp);
        for(Annotation anno : method.getAnnotations()){
            System.out.println(anno.annotationType());
            // Annotation details
        }
        
        // Logged in user details
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        String currentUserName = authentication.getName();
        //or
        //Get the HttpServletRequest currently bound to the thread.
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
                .getRequest();
        
        // Logic to proceed to the controller method based on the logged in user or request can be done here. 
        
        try {
            retObject = pjp.proceed();
        } catch (Throwable e) {
            // Handle the exception
        }
        
        return retObject;
    }
    
    //Refer : https://stackoverflow.com/q/5714411/4214241
    private Method getMethod(ProceedingJoinPoint pjp) {
        MethodSignature signature = (MethodSignature) pjp.getSignature();
        Method method = signature.getMethod();
        if (method.getDeclaringClass().isInterface()) {
            try {
                method= pjp.getTarget().getClass().getDeclaredMethod(pjp.getSignature().getName(),
                        method.getParameterTypes());
            } catch (final SecurityException exception) {
                //...
            } catch (final NoSuchMethodException exception) {
                //...                
            }
        }   
        return method;
    }

}

Also note that the annotation RetentionPolicy must be RUNTIME , for this code to work.

R.G
  • 6,436
  • 3
  • 19
  • 28