0
点赞
收藏
分享

微信扫一扫

springboot 自定义注解+拦截器+Redis实现限流,防止恶意刷接口实践

自定义注解,具体频次,根据具体场景设置

import java.lang.annotation.*;

@Inherited
@Documented
@Target({ElementType.FIELD, ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
/**
* 限流注解
*/
public @interface AccessLimit {
//标识 指定sec时间段内的访问次数限制
int limit() default 5;

//标识 时间段
int sec() default 5;
}

拦截器

import com.zubus.commonUtils.entity.BusyBusinessException;
import com.zubus.commonUtils.tool.IpUtils;
import com.zubus.mobileApi.annotation.AccessLimit;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

/**
* @description: 限流拦截器
* @author: x x
* @create: 2021-11-18 09:45
*/
@Component
public class AccessLimitInterceptor implements HandlerInterceptor {

//使用RedisTemplate操作redis
@Autowired
private RedisTemplate redisTemplate;

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
Method method = handlerMethod.getMethod();
if (!method.isAnnotationPresent(AccessLimit.class)) {
return true;
}
AccessLimit accessLimit = method.getAnnotation(AccessLimit.class);
if (accessLimit == null) {
return true;
}
int limit = accessLimit.limit();
int sec = accessLimit.sec();
String key = IpUtils.getIpAddr(request) + request.getRequestURI();
Integer maxLimit = (Integer) redisTemplate.opsForValue().get(key);
if (maxLimit == null) {
//set时一定要加过期时间
redisTemplate.opsForValue().set(key, 1, sec, TimeUnit.SECONDS);
} else if (maxLimit < limit) {
redisTemplate.opsForValue().set(key, maxLimit + 1, sec, TimeUnit.SECONDS);
} else {
throw new BusyBusinessException("请求太频繁");
//output(response, "请求太频繁!");
//return false;
}
}
return true;
}

public void output(HttpServletResponse response, String msg) throws IOException {
response.setContentType("application/json;charset=UTF-8");
ServletOutputStream outputStream = null;
try {
outputStream = response.getOutputStream();
outputStream.write(msg.getBytes("UTF-8"));
} catch (IOException e) {
e.printStackTrace();
} finally {
outputStream.flush();
outputStream.close();
}
}

@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

}

@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

}
}

注册/配置拦截器

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

import javax.annotation.Resource;

/**
* @description: 拦截器注册
* @author: x x
* @create: 2021-11-18 09:57
*/
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {

@Resource
private AccessLimitInterceptor accessLimitInterceptor;

@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(accessLimitInterceptor);
}
}

自定义异常

public class BusyBusinessException extends RuntimeException{

public BusyBusinessException() {}
public BusyBusinessException(String exceptionInfo) {
super(exceptionInfo);
}
}

全局异常处理

@RestController
@Log4j2
public class GlobalException {
@ControllerAdvice
public class GlobalControllerExceptionHandler {
/*
* 统一异常处理
*/
@ExceptionHandler(value = Exception.class)
@ResponseBody// 返回json数据
public ApiResult defaultErrorHandler(HttpServletRequest req, Exception e) throws Exception {
ApiResult api = new ApiResult();
String info = "系统异常,请联系管理员";
e.printStackTrace();
if(e instanceof UnauthorizedException) {
info = "没有权限";
//throw e;
}else if(e instanceof HttpRequestMethodNotSupportedException) {
throw e;
//api.setCode(405);
//return api.failed("请先登录");
}else if(e instanceof MaxUploadSizeExceededException) {
info = "文件太大";
}else if (e instanceof BusyBusinessException) {
return api.failed(e.getMessage());
}
return api.failed(info);
}
}
}

测试接口

@GetMapping("/noAuth/hello")
@AccessLimit()
public ApiResult hello(HttpSession session) {


return new ApiResult().success("helle");

}

测试,连续访问6次

springboot 自定义注解+拦截器+Redis实现限流,防止恶意刷接口实践_mysql

springboot 自定义注解+拦截器+Redis实现限流,防止恶意刷接口实践_redis_02

本地测试达到预期效果,线上测试,有待观察,仅供参考。


举报

相关推荐

0 条评论