1. 程式人生 > >限制http介面請求次數

限制http介面請求次數

思路:利用aop實現根據ip限制一個介面在一段時間內的請求次數
實現方式用了redis,直接設定key的過期時間

先來個註解:

import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;

import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
@Order(Ordered.HIGHEST_PRECEDENCE)
public
@interface RequestLimit { /** * 允許訪問的次數,預設值MAX_VALUE */ int count() default Integer.MAX_VALUE; /** * 時間段,單位為毫秒,預設值一分鐘 */ long time() default 60000; }

接下來是限制邏輯,2種實現方式,hashMap和redis(註釋的內容):

import com.annotation.RequestLimit;
import com.core.websocket.WebSocket;
import com
.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.beans.factory.annotation.Autowired; import org.springframework
.data.redis.core.RedisTemplate; import javax.servlet.http.HttpServletRequest; import java.util.LinkedList; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; @Aspect public class RequestLimitAop { private static final Logger logger = LoggerFactory.getLogger(RequestLimitAop.class); @Autowired private RedisTemplate redisTemplate; @Before("within(@org.springframework.stereotype.Controller *) && @annotation(limit)") public void requestLimit(JoinPoint joinPoint, RequestLimit limit) throws RequestLimitException { try { Object[] args = joinPoint.getArgs(); HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest(); String ip = WebSocket.getIpAddress(request); String url = request.getRequestURL().toString(); String key = "req_limit_".concat(url).concat("_").concat(ip); boolean checkResult = checkWithRedis(limit, key); if (!checkResult) { logger.debug("requestLimited," + "[使用者ip:{}],[訪問地址:{}]超過了限定的次數[{}]次", ip, url, limit.count()); throw new RequestLimitException(); } } catch (RequestLimitException e) { throw e; } catch (Exception e) { logger.error("RequestLimitAop.requestLimit has some exceptions: ", e); } } /** * 以redis實現請求記錄 * * @param limit * @param key * @return */ private boolean checkWithRedis(RequestLimit limit, String key) { long count = redisTemplate.opsForValue().increment(key, 1); if (count == 1) { redisTemplate.expire(key, limit.time(), TimeUnit.MILLISECONDS); } if (count > limit.count()) { return false; } return true; } }

最後就是controller了:

    @RequestMapping(value = "limit")
    @RequestLimit(count = 2)
    public String requestLimit(HttpServletRequest request) {
        return "test";
    }

專案裡面還需要開啟aop的配置:

@Configuration
@EnableAspectJAutoProxy
@ComponentScan(basePackages = {"com.aop"})
public class AopConfig {

    @Bean
    public RequestLimitAop requestLimitAop() {
        return new RequestLimitAop();
    }

}

RequestLimitException是自定義的一個RuntimeException子類,在controller層需要處理這個異常:

import com.exception.RequestLimitException;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

@ControllerAdvice(basePackages = {"com.web.controller"})
public class ExceptionController {

    @ExceptionHandler(RequestLimitException.class)
    public String requestLimitException(Model model) {
        model.addAttribute("errorMsg", "請求過於頻繁,超出限制!");
        return "error";
    }

}