1.maven导入坐标
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</dependency>
2.定义注解
import java.lang.annotation.*;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
@Order(Ordered.HIGHEST_PRECEDENCE)
public @interface RequestLimit {
/**
* 允许访问的最大次数
*/
int count() default Integer.MAX_VALUE;
/**
* 时间段,单位为毫秒,默认值一分钟
*/
long time() default 60000;
}
3.定义异常类
public class RequestLimitException extends Exception{
private static final long serialVersionUID = 1555967171104727461L;
public RequestLimitException(){
super("HTTP请求超出设定的限制");
}
public RequestLimitException(String message){
super(message);
}
}
4. 业务逻辑,存入redis路径,设置过期时间
import org.apache.commons.lang3.StringUtils;
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 org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
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(RequestLimitContract.class);
@Autowired
private RedisTemplate<String, String> redisTemplate;
private static String limitPath = "/safeLimit/limit";
@Before("@annotation(limit)")
public void requestLimit(final JoinPoint joinPoint, RequestLimit limit) throws RequestLimitException {
try {
Object[] args = joinPoint.getArgs();
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletResponse response = servletRequestAttributes.getResponse();
for (int i = 0; i < args.length; i++) {
if (args[i] instanceof HttpServletRequest) {
request = (HttpServletRequest) args[i];
break;
}
}
if (request == null) {
throw new RequestLimitException("方法中缺失HttpServletRequest参数");
}
String ip = request.getRemoteAddr();
String url = request.getRequestURL().toString();
String key = "req_limit_".concat(url).concat(ip);
if (!redisTemplate.hasKey(key) || StringUtils.isEmpty(redisTemplate.opsForValue().get(key))) {
redisTemplate.opsForValue().set(key, String.valueOf(1), limit.time(), TimeUnit.MILLISECONDS);
} else {
Integer getValue = Integer.parseInt(redisTemplate.opsForValue().get(key)) + 1;
redisTemplate.opsForValue().set(key, String.valueOf(getValue), limit.time(), TimeUnit.MILLISECONDS);
}
int count = Integer.parseInt(redisTemplate.opsForValue().get(key));
if (count == 1) {
//创建一个定时器
TimerTask timerTask = new TimerTask() {
@Override
public void run() {
redisTemplate.delete(key);
}
};
//这个定时器设定在time规定的时间之后会执行上面的remove方法,也就是说在这个时间后它可以重新访问
new Timer().schedule(timerTask, limit.time());
}
if (count > limit.count()) {
logger.info("用户IP[" + ip + "]访问地址[" + url + "]超过了限定的次数[" + limit.count() + "]");
// throw new RequestLimitException();
//端口号
String toLomitPath = "http://" + request.getServerName() + ":" + request.getServerPort() + limitPath;
response.sendRedirect(toLomitPath);
}
} catch (RequestLimitException e) {
throw e;
} catch (Exception e) {
logger.error("发生异常", e);
}
}
}
5.编写异常重定向类
@RestController
@RequestMapping("/safeLimit")
public class SafeLimitController {
@RequestMapping("/limit")
public MessageInfo limit(){
// 这里用自己的接口返回值就行
return ResultGenerator.genFailResult("超出请求限制");
}
}
6.接口上添加注解
@RequestLimit(count = 3, time = 10000)
count为次数,time为时间(毫秒)