场景
SpringBoot搭建的项目需要对开放的接口进行防刷限制,不同接口指定多少秒内可以请求指定次数。
比如下方限制接口一秒内最多请求一次。
注:
博客:
霸道流氓气质的博客_CSDN博客-C#,架构之路,SpringBoot领域博主
实现
1、实现思路
首先自定义注解,添加时间区间和最大请求次数字段。
然后自定义拦截器拦截方法上面是否有自定义的注解,如果有则获取请求的url,并作为redis中存储的key,区间区间即作为redis
的key的过期时间,每次请求累加次数并存储到redis中,判断在redis的key有效期内超过最大请求次数,则返回请求频繁的响应。
2、首先自定义注解实现
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface AccessLimit {
/**
* 秒数时间区间(多少秒内)
* @return
*/
int seconds();
/**
* 最大请求次数
* @return
*/
int maxCount();
}
3、然后自定义拦截实现HandlerInterceptor接口,并重写其preHandle方法,实现方法请求前的拦截
import com.alibaba.fastjson.JSON;
import com.badao.demo.common.AjaxResult;
import com.badao.demo.constant.Constants;
import com.badao.demo.utils.RedisCache;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.OutputStream;
import java.util.concurrent.TimeUnit;
@Component
public class FangshuaInterceptor implements HandlerInterceptor {
@Autowired
private RedisCache redisService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//判断请求是否属于方法的请求
if(handler instanceof HandlerMethod){
HandlerMethod hm = (HandlerMethod) handler;
//获取方法中的注解,看是否有该注解
AccessLimit accessLimit = hm.getMethodAnnotation(AccessLimit.class);
if(accessLimit == null){
return true;
}
int seconds = accessLimit.seconds();
int maxCount = accessLimit.maxCount();
//获取请求的Url作为redis中存储的key
String key = request.getRequestURI();
//从redis中获取用户访问的次数
Integer count = redisService.getCacheObject(key);
if(count == null){
//第一次访问
redisService.setCacheObject(key,1,seconds, TimeUnit.SECONDS);
}else if(count < maxCount){
//加1
count+=1;
redisService.setCacheObject(key,count,seconds, TimeUnit.SECONDS);
}else{
//超出访问次数,则渲染响应结果
render(response);
return false;
}
}
return true;
}
/**
* 接口渲染
* @param response
* @throws Exception
*/
private void render(HttpServletResponse response)throws Exception {
response.setContentType("application/json;charset=UTF-8");
OutputStream out = response.getOutputStream();
String str = JSON.toJSONString(AjaxResult.error(Constants.CALL_TOO_OFEN));
out.write(str.getBytes("UTF-8"));
out.flush();
out.close();
}
}
其中RedisCache是封装的操作redis的工具类
AjaxResult是封装的响应结果实体。
Constants.CALL_TOO_OFEN是常量类用来提示请求太频繁。
以上工具类的封装可以参考
若依前后端分离版手把手教你本地搭建环境并运行项目:
https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/108465662
4、然后将intercepter注册到springboot中
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* 把intercepror 注册到springboot中
*/
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private FangshuaInterceptor interceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(interceptor);
}
}
5、使用时在接口方法上添加注解并指定时间和最大请求次数
@AccessLimit(seconds = 1,maxCount = 1)
@GetMapping("/test")
public AjaxResult test(@RequestParam("mineApiCode") String mineApiCode)
上面参数设置就是1秒内最大请求次数为1次。