目的:用户提交请求过于频繁的限制
代码实现:
限流
步骤一:自定义限流设置(自定义注解详见本人简文篇:https://www.jianshu.com/p/0c342c34932b)
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface AccessLimit {
//指定second 时间内 API请求次数
int times()default 2;
// 请求次数的指定时间范围 秒数(redis数据过期时间)
int second()default 60;
}
步骤二:指定需要限流的接口使用自定义注解
/**
* 发送验证码接口
*/
@AccessLimit
@PostMapping("/sendVerificationCode")
public ResultsendVerificationCode(@RequestBody mobile m) {
XXX
}
配置拦截器
步骤一: 设置登录拦截器
/**
* 设置登录拦截器
*/
@Aspect
@Component
public class AccessLimitInterceptorimplements HandlerInterceptor{
private final static Loggerlogger = LoggerFactory.getLogger(AccessLimitInterceptor.class);
@Resource
private RedisTemplateredisTemplate;
private static final StringTEMPLE_CODE ="ACCESSLIMT_";
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {
try {
// Handler 是否为 HandlerMethod 实例
if (handlerinstanceof HandlerMethod) {
// 强转
HandlerMethod handlerMethod = (HandlerMethod) handler;
// 获取方法
Method method = handlerMethod.getMethod();
// 是否有AccessLimit注解
if (!method.isAnnotationPresent(AccessLimit.class)) {
return true;
}
// 获取注解内容信息
AccessLimit accessLimit = method.getAnnotation(AccessLimit.class);
if (accessLimit ==null) {
return true;
}
int times = accessLimit.times();//请求次数
int second = accessLimit.second();//请求时间范围
//根据 IP + API 限流
String key =TEMPLE_CODE+RequestUtil.getIpAddr(request);
//根据key获取已请求次数
Integer maxTimes =redisTemplate.opsForValue().get(key);
if (maxTimes ==null) {
//set时一定要加过期时间
redisTemplate.opsForValue().set(key, 1, second, TimeUnit.SECONDS);
}else if (maxTimes < times) {
redisTemplate.opsForValue().set(key, maxTimes +1, second, TimeUnit.SECONDS);
}else {
Result result =new Result();
result.setMsg("调用过于频繁,请稍后重试");
result.setCode(ResultEnum.ERROR.getCode());
RequestUtil.responseOut(response, result);
return false;
}
}
}catch (Exception e) {
logger.error("请求限流拦截异常,请检查Redis是否开启!", e);
throw new MyException(ResultEnum.ERROR.getCode(),"请求限流异常");
}
return true;
}
}
@Component
class RequestUtil {
private final static Loggerlogger = LoggerFactory.getLogger(RequestUtil.class);
/**
* IpUtils工具类方法
* 获取真实的ip地址
*/
public static StringgetIpAddr(HttpServletRequest request) {
String ip = request.getHeader("X-Forwarded-For");
if (StringUtils.isNotEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)) {
//多次反向代理后会有多个ip值,第一个ip才是真实ip
int index = ip.indexOf(",");
if (index != -1) {
return ip.substring(0, index);
}else {
return ip;
}
}
ip = request.getHeader("X-Real-IP");
if (StringUtils.isNotEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)) {
return ip;
}
return request.getRemoteAddr();
}
/**
* @param response : 响应请求
* @param object: object
* @return void
* @Title: out
* @Description: response输出JSON数据
**/
public static void responseOut(ServletResponse response, Object object) {
PrintWriter out =null;
try {
response.setContentType("application/json;charset=UTF-8");
response.setCharacterEncoding("UTF-8");
out = response.getWriter();
out.println(JSONObject.toJSONString(object));
}catch (Exception e) {
logger.error("响应出错:{}", e);
e.printStackTrace();
}finally {
if (null != out) {
out.flush();
out.close();
}
}
}
}
设置拦截器
@Configuration
public class ApplicationConfigimplements WebMvcConfigurer {
@Autowired
private AccessLimitInterceptoraccessLimitInterceptor;
/**
* 配置拦截器
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
//API限流拦截
registry.addInterceptor(accessLimitInterceptor)
//可添加多个(/**对所有请求进行拦截)
.addPathPatterns("/**")
}
}