当前位置:   article > 正文

Sprintboot 防止重复请求_springboot防止接口重复请求

springboot防止接口重复请求

一、过滤器

过滤器配置

/*
* 重复请求过滤器
* */
@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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

过滤器

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);
        }
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

过滤器指定包装实现

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;
            }
        };
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55

二、拦截器

拦截器抽象

@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);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

重点方法isRepeatSubmit

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;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96

三、防重复注解

@Inherited
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RepeatSubmit {

    public int interval() default 5000;
    public String message() default "不允许重复提交,请稍后再试";
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

四、最终使用

@RequestMapping("/test/repeat")
@RestController
public class TestRepeatController {


    @RepeatSubmit(interval = 1000,message = "请求过于频繁,请稍后重试~")
    @RequestMapping("/add")
    public String addUser(String id) {
        return "ok";
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

五、总结

所谓重复请求,就是两个请求体里内容相同,我们要做的就是比对两个请求。
过滤器主要作用是保证请求体内字符编码相同
拦截器拦截我们指定的方法,将相同请求地址封装成key,请求体内容为value
保存到redis缓存数据库,并设置失效时间
进行比对即可。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/234299
推荐阅读
相关标签
  

闽ICP备14008679号