Custom annotations in SpringBoot implement controller access limit instances

  • 2020-06-23 00:15:07
  • OfStack

Today I'm going to show you how custom annotations in SpringBoot can limit the number of controller accesses.

One of the most common attacks in Web is the use of malicious URL to access a maeled-out server. Today I'll show you how to use custom annotations to implement a defense against such attacks.

The solution to this problem is to add custom annotations to the controller to limit the number of accesses.

See the following example for the specific implementation process:

Step 1: Define an annotation class, and look at the code example:


package example.controller.limit; 
import org.springframework.core.Ordered; 
import org.springframework.core.annotation.Order; 
import java.lang.annotation.*; 
@Retention(RetentionPolicy.RUNTIME) 
@Target(ElementType.METHOD) 
@Documented 
// Highest priority  
@Order(Ordered.HIGHEST_PRECEDENCE) 
public @interface RequestLimit { 
  /** 
   * 
   *  The number of accesses allowed, by default MAX_VALUE 
   */ 
  int count() default Integer.MAX_VALUE; 
 
  /** 
   * 
   *  Time period in milliseconds, default 1 minutes  
   */ 
  long time() default 60000; 
} 

Step 2: Define an exception class to handle the exception problem caused by the URL attack. Here is the code example:


package example.controller.exception; 
public class RequestLimitException extends Exception { 
  private static final long serialVersionUID = 1364225358754654702L; 
 
  public RequestLimitException() { 
    super("HTTP Request exceeds set limit "); 
  } 
 
  public RequestLimitException(String message) { 
    super(message); 
  } 
 
} 

Step 3: Define a concrete implementation class for an annotation. Look at the code example below:


package example.controller.limit; 
import example.controller.exception.RequestLimitException; 
import org.aspectj.lang.JoinPoint; 
import org.aspectj.lang.annotation.Aspect; 
import org.aspectj.lang.annotation.Before; 
import org.slf4j.Logger; 
import org.slf4j.LoggerFactory; 
import org.springframework.stereotype.Component; 
import javax.servlet.http.HttpServletRequest; 
import java.util.HashMap; 
import java.util.Map; 
import java.util.Timer; 
import java.util.TimerTask; 
import java.util.concurrent.TimeUnit; 
 
@Aspect 
@Component 
public class RequestLimitContract { 
  private static final Logger logger = LoggerFactory.getLogger("RequestLimitLogger"); 
  private Map<String, Integer> redisTemplate=new HashMap<String,Integer>(); 
  @Before("within(@org.springframework.stereotype.Controller *) && @annotation(limit)") 
  public void requestLimit(final JoinPoint joinPoint, RequestLimit limit) throws RequestLimitException { 
    try { 
      Object[] args = joinPoint.getArgs(); 
      HttpServletRequest request = null; 
      for (int i = 0; i < args.length; i++) { 
        if (args[i] instanceof HttpServletRequest) { 
          request = (HttpServletRequest) args[i]; 
          break; 
        } 
      } 
      if (request == null) { 
        throw new RequestLimitException(" Missing in the method HttpServletRequest parameter "); 
      } 
      String ip = request.getLocalAddr(); 
      String url = request.getRequestURL().toString(); 
      String key = "req_limit_".concat(url).concat(ip); 
      if(redisTemplate.get(key)==null || redisTemplate.get(key)==0){ 
        redisTemplate.put(key,1); 
      }else{ 
        redisTemplate.put(key,redisTemplate.get(key)+1); 
      } 
      int count = redisTemplate.get(key); 
      if (count > 0) { 
        Timer timer= new Timer(); 
        TimerTask task = new TimerTask(){  // create 1 A new timer task.  
          @Override 
          public void run() { 
            redisTemplate.remove(key); 
          } 
        }; 
        timer.schedule(task, limit.time()); 
        // Schedules the execution of a specified task after a specified delay. task :  The task to be arranged. 10000 :  The delay before a task is executed, in milliseconds.  
      } 
      if (count > limit.count()) { 
        //logger.info(" The user IP[" + ip + "] Access to the address [" + url + "] Exceeds the limit number of times [" + limit.count() + "]"); 
        throw new RequestLimitException(); 
      } 
    } catch (RequestLimitException e) { 
      throw e; 
    } catch (Exception e) { 
      logger.error(" An exception occurs : ", e); 
    } 
  } 
} 

Step 4: Implement a control class and add the ability to use annotations. Here's a code example:


package example.controller; 
import example.controller.limit.RequestLimit; 
import org.springframework.stereotype.Controller; 
import org.springframework.ui.ModelMap; 
import org.springframework.web.bind.annotation.RequestMapping; 
import org.springframework.web.bind.annotation.ResponseBody; 
import javax.servlet.http.HttpServletRequest; 
@Controller 
public class URLController { 
  @RequestLimit(count=10,time=5000) 
  @RequestMapping("/urltest") 
  @ResponseBody 
  public String test(HttpServletRequest request, ModelMap modelMap) { 
    return "aaa"; 
  } 
} 

count refers to the number of visits within a specified time, while time refers to the specified time in milliseconds.

This implements the url interception at the controller level. The problem is that this is not enough to block every URL page. Since it is not possible to annotate every controller with url intercepts, this approach is only appropriate for certain URL intercepts.

How do you implement URL access interception at the filter level? In the next section, I will show you how to implement URl access interception by using filters and ip blacklist by using JPA. After joining the IP blacklist, no URL access is allowed.


Related articles: