I am adding a speed limit for a calm webservice using Spring MVC 4.1.
I created an @RateLimited annotation that I can apply to controller methods. A Spring AOP aspect intercepts calls to these methods and throws an exception if there are too many requests:
@Aspect @Component @Order(Ordered.HIGHEST_PRECEDENCE) public class RateLimitingAspect { @Autowired private RateLimitService rateLimitService; @Before("execution(* com.example..*.*(.., javax.servlet.ServletRequest+, ..)) " + "&& @annotation(com.example.RateLimited)") public void wait(JoinPoint jp) throws Throwable { ServletRequest request = Arrays .stream(jp.getArgs()) .filter(Objects::nonNull) .filter(arg -> ServletRequest.class.isAssignableFrom(arg.getClass())) .map(ServletRequest.class::cast) .findFirst() .get(); String ip = request.getRemoteAddr(); int secondsToWait = rateLimitService.secondsUntilNextAllowedAttempt(ip); if (secondsToWait > 0) { throw new TooManyRequestsException(secondsToWait); } }
All this works fine, except when the @RateLimited controller @RateLimited has parameters marked as @Valid , for example:
@RateLimited @RequestMapping(method = RequestMethod.POST) public HttpEntity<?> createAccount( HttpServletRequest request, @Valid @RequestBody CreateAccountRequestDto dto) { ... }
Problem: if the test fails, the validator throws a MethodArgumentNotValidException , which is handled by @ExceptionHandler , which returns the clientβs response to the client, never calling my @Before and therefore bypassing the speed limit.
How can I intercept such a web request in a way that takes precedence over parameter validation?
I was thinking about using Spring hooks or simple servlet filters, but they are displayed using simple url templates, and I need to distinguish between GET / POST / PUT, etc.
spring-mvc spring-security rate-limiting
Alex wittig
source share