赞
踩
/* * 重复请求过滤器 * */ @Bean public FilterRegistrationBean repeatFilterRegistration(){ FilterRegistrationBean registrationBean = new FilterRegistrationBean(); //设置一个filter registrationBean.setFilter(new RepeatableFilter()); //设置filter生效地址 registrationBean.addUrlPatterns("/*"); //设置filter的名字 registrationBean.setName("repeatableFilter"); //设置filter优先级 registrationBean.setOrder(FilterRegistrationBean.LOWEST_PRECEDENCE); return registrationBean; }
public class RepeatableFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { ServletRequest servletRequest = null; if (request instanceof HttpServletRequest && StringUtils.startsWithIgnoreCase(request.getContentType(), MediaType.APPLICATION_JSON_VALUE)){ servletRequest = new RepeatedlyRequestWarpper((HttpServletRequest) request, response); } if (null == servletRequest){ chain.doFilter(request,response); }else { chain.doFilter(servletRequest,response); } } }
public class RepeatedlyRequestWarpper extends HttpServletRequestWrapper { private final byte [] body; /** * Constructs a request object wrapping the given request. * * @param request The request to wrap * @throws IllegalArgumentException if the request is null */ public RepeatedlyRequestWarpper(HttpServletRequest request, ServletResponse response) throws IOException{ super(request); request.setCharacterEncoding(Constants.UTF8); response.setCharacterEncoding(Constants.UTF8); body = HttpUtil.getString(request.getInputStream(), StandardCharsets.UTF_8,false).getBytes(StandardCharsets.UTF_8); } @Override public BufferedReader getReader() throws IOException { return new BufferedReader(new InputStreamReader(getInputStream())); } @Override public ServletInputStream getInputStream() throws IOException { final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body); return new ServletInputStream() { @Override public boolean isFinished() { return false; } @Override public boolean isReady() { return false; } @Override public void setReadListener(ReadListener listener) { } @Override public int read() throws IOException { return byteArrayInputStream.read(); } @Override public int available() throws IOException { return body.length; } }; } }
@Component public abstract class RepeatSubmitInterceptor implements HandlerInterceptor{ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if (handler instanceof HandlerMethod){ HandlerMethod handlerMethod = (HandlerMethod)handler; Method method = handlerMethod.getMethod(); RepeatSubmit annotition = method.getAnnotation(RepeatSubmit.class); if (annotition != null){ if (this.isRepeatSubmit(request,annotition)){ JSONResult result = JSONResult.error(annotition.message()); ServletUtils.renderString(response, JSON.toJSONString(result)); return false; } } return true; }else { return true; } } public abstract boolean isRepeatSubmit(HttpServletRequest request,RepeatSubmit annotion); }
public class SameUrlDataInterceptor extends RepeatSubmitInterceptor { public final String REPEAT_PARAMS = "repeatParams"; public final String REPEAT_TIME = "repeatTime"; // 令牌自定义标识 @Value("${token.header}") private String header; @Autowired private RedisCache redisCache; /** * 判断是不是重复提交 * @param request * @param annotion * @return */ @Override public boolean isRepeatSubmit(HttpServletRequest request, RepeatSubmit annotion) { //定义一个本次请求的参数 String nowParams = ""; /* * instanceof关键字,测试它左边的对象是否是它右边的类的实例,判断有没有经过重复包装(utf8字符集转换) * RepeatedlyRequestWarpper---继承-----》HttpServletRequestWrapper----继承-----》HttpServletRequest=HttpServletRequest * * */ if (request instanceof RepeatedlyRequestWarpper){ RepeatedlyRequestWarpper repeatedlyRequestWarpper = (RepeatedlyRequestWarpper) request; nowParams = HttpUtils.getBodyString(request); } if (StringUtils.isNull(nowParams) || StringUtils.isEmpty(nowParams)){ nowParams = JSON.toJSONString(request.getParameterMap()); } //获取请求体参数后存储到一个map中 Map<String,Object> nowDataMap = new HashMap<>(); nowDataMap.put(REPEAT_PARAMS,nowParams); nowDataMap.put(REPEAT_TIME,System.currentTimeMillis()); //获取访问地址 String url = request.getRequestURI(); //获得token令牌 String submitKey = StringUtils.trimToEmpty(request.getHeader(header)); //拼接成最终的redis key(这个只能说明一个令牌在一个访问地址) String cacheRepeatKey = CacheConstants.REPEAT_SUBMIT_KEY+url+submitKey; //尝试去redis中查找有没有对应的key Object cacheObject = redisCache.getCacheObject(cacheRepeatKey); if (cacheObject != null){ //将redis中查到的对象强转 Map<String,Object> cacheMap = (Map<String,Object>)cacheObject; if (cacheMap.containsKey(url)){ Map<String,Object> preDataMap = (Map<String, Object>)cacheMap.get(url); if (compareParams(nowDataMap,preDataMap) && compareTime(nowDataMap,preDataMap,annotion.interval())){ return true; } } } //查不到说明redis中没有这个访问地址的请求,进行缓存 Map<String,Object> cacheMap = new HashMap<String,Object>(); //key是访问地址,value是访问参数 cacheMap.put(url,nowDataMap); redisCache.setCacheObject(cacheRepeatKey,annotion.interval(),annotion.interval(), TimeUnit.MILLISECONDS); return false; } /** 判断参数是否相同 * @param nowMap * @param preMap * @return */ private boolean compareParams(Map<String,Object> nowMap, Map<String,Object> preMap){ String nowParams = (String) nowMap.get(REPEAT_PARAMS); String preParams = (String) preMap.get(REPEAT_PARAMS); return nowParams.equals(preParams); } /** 判断两次间隔时间 * @param nowMap * @param preMap * @param interval * @return */ private boolean compareTime(Map<String, Object> nowMap, Map<String, Object> preMap, int interval) { long time1 = (Long) nowMap.get(REPEAT_TIME); long time2 = (Long) preMap.get(REPEAT_TIME); if ((time1 - time2) < interval) { return true; } return false; } }
@Inherited
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RepeatSubmit {
public int interval() default 5000;
public String message() default "不允许重复提交,请稍后再试";
}
@RequestMapping("/test/repeat")
@RestController
public class TestRepeatController {
@RepeatSubmit(interval = 1000,message = "请求过于频繁,请稍后重试~")
@RequestMapping("/add")
public String addUser(String id) {
return "ok";
}
}
所谓重复请求,就是两个请求体里内容相同,我们要做的就是比对两个请求。
过滤器主要作用是保证请求体内字符编码相同
拦截器拦截我们指定的方法,将相同请求地址封装成key,请求体内容为value
保存到redis缓存数据库,并设置失效时间
进行比对即可。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。