第九章🚦自定义注解-防止重复提交

297 阅读2分钟

🧑‍🎓 个人主页:花棉袄

📖 本章内容:【自定义注解-防止重复提交

在这里插入图片描述

🌼SpringBoot:搭建后端项目👉 Gitee仓库地址

🥗Redis配置

🥙自定义注解

/**
 * Author:  花棉袄
 * Date:  2022年08月19日
 * Describe:  自定义注解:限制表单重复提交
 */
@Inherited
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface NotRepeatSubmit {
    @ApiModelProperty("间隔时间(ms),小于此时间视为重复提交")
    int interval() default 500;


    @ApiModelProperty("提示消息")
    String message() default "不允许重复提交,请稍候再试!!!!";
}

🍑防止重复提交拦截器

/**
 * Author: 花棉袄
 * CreateDate: 2022年08月23日
 * Describe: 防止重复提交拦截器
 */
@Component
public abstract class RepeatSubmitInterceptor implements HandlerInterceptor {
}
  • 👉🏽重写preHandle方法
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    if (handler instanceof HandlerMethod) {
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        Method method = handlerMethod.getMethod();
        NotRepeatSubmit notRepeatSubmit = method.getAnnotation(NotRepeatSubmit.class);
        if (notRepeatSubmit != null) {
            if (this.isRepeatSubmit(request, notRepeatSubmit)) {
                AjaxResult ajaxResult = AjaxResult.error(notRepeatSubmit.message());
                ServletUtils.renderString(response, JSON.toJSONString(ajaxResult));
                return false;
            }
        }
    }
    return true;
}
  • 👉验证是否重复提交
/**
 * 验证是否重复提交
 * 由子类实现具体地防止重复提交的规则
 *
 * @param request
 * @return
 * @throws Exception
 */
public abstract boolean isRepeatSubmit(HttpServletRequest request, NotRepeatSubmit notRepeatSubmit);
  • 👉🏽由子类实现具体的防止重复提交的规则
@Component
public class SameUrlDataInterceptor extends RepeatSubmitInterceptor {

    public final String REPEAT_PARAMS = "repeatParams";
    public final String REPEAT_TIME = "repeatTime";
    public final String HEADER = "Authorization";

    @Autowired
    private RedisUtils redisUtils;
    /**
     * 判断参数是否相同
     */
    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);
    }

    /**
     * 判断两次间隔时间
     */
    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;
    }
 }
  • 👉🏽获取请求体中的数据
/**
 * @Author: 花棉袄
 * @CreateDate: 2022年08月23日
 * @Describe: 获取请求参数
 */
public class HttpHelperUtils {
    private static final Logger LOGGER = LoggerFactory.getLogger(HttpHelperUtils.class);

    public static String getBodyString(ServletRequest request) {
        StringBuilder sb = new StringBuilder();
        BufferedReader reader = null;
        try (InputStream inputStream = request.getInputStream()) {
            reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
            String line = "";
            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }
        } catch (IOException e) {
            LOGGER.warn("getBodyString出现问题!");
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    LOGGER.error(ExceptionUtils.getMessage(e));
                }
            }
        }
        return sb.toString();
    }
}
/**
 * 校验是否是重复提交
 *
 * @param request
 * @param notRepeatSubmit
 * @return
 */
@Override
public boolean isRepeatSubmit(HttpServletRequest request, NotRepeatSubmit notRepeatSubmit) {
    String nowParams = "";
    if (request instanceof RepeatedlyRequestWrapper) {
        RepeatedlyRequestWrapper repeatedlyRequestWrapper = (RepeatedlyRequestWrapper) request;
        nowParams = HttpHelperUtils.getBodyString(repeatedlyRequestWrapper);
    }

    // body参数为空,获取Parameter的数据
    if (StringUtils.isEmpty(nowParams)) {
        nowParams = JSON.toJSONString(request.getParameterMap());
    }
    Map<String, Object> nowDataMap = new HashMap<String, Object>();
    nowDataMap.put(REPEAT_PARAMS, nowParams);
    nowDataMap.put(REPEAT_TIME, System.currentTimeMillis());

    // 请求地址(作为存放cache的key值)
    String url = request.getRequestURI();

    // 唯一值(没有消息头则使用请求地址)
    String submitKey = StringUtils.trimToEmpty(request.getHeader(HANDER));

    // 唯一标识(指定key + url + 消息头)
    String cacheRepeatKey = RedisCacheConstant.REPEAT_SUBMIT_KEY + url + submitKey;

    Object sessionObj = redisUtils.getCacheObject(cacheRepeatKey);
    if (sessionObj != null) {
        Map<String, Object> sessionMap = (Map<String, Object>) sessionObj;
        if (sessionMap.containsKey(url)) {
            Map<String, Object> preDataMap = (Map<String, Object>) sessionMap.get(url);
            if (compareParams(nowDataMap, preDataMap) && compareTime(nowDataMap, preDataMap, notRepeatSubmit.interval())) {
                return true;
            }
        }
    }
    Map<String, Object> cacheMap = new HashMap<String, Object>();
    cacheMap.put(url, nowDataMap);
    redisUtils.setCacheObject(cacheRepeatKey, cacheMap, notRepeatSubmit.interval(), TimeUnit.MILLISECONDS);
    return false;
}

🍏构建可重复读取的流

/**
 * Author:  花棉袄
 * Date:  2022年08月19日
 * Describe:  构建可重复读取inputStream的request
 */
public class RepeatedlyRequestWrapper extends HttpServletRequestWrapper {
    private final byte[] body;

    public RepeatedlyRequestWrapper(HttpServletRequest request, ServletResponse response) throws IOException {
        super(request);
        request.setCharacterEncoding(UTF8);
        response.setCharacterEncoding(UTF8);

        body = HttpHelperUtils.getBodyString(request).getBytes(UTF8);
    }

    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(getInputStream()));
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        final ByteArrayInputStream basis = new ByteArrayInputStream(body);
        return new ServletInputStream() {
            @Override
            public int read() throws IOException {
                return basis.read();
            }

            @Override
            public int available() throws IOException {
                return body.length;
            }

            @Override
            public boolean isFinished() {
                return false;
            }

            @Override
            public boolean isReady() {
                return false;
            }

            @Override
            public void setReadListener(ReadListener readListener) {

            }
        };
    }
}

🍖注册拦截器

@Configuration
public class ResourcesConfig implements WebMvcConfigurer {
    @Autowired
    private RepeatSubmitInterceptor repeatSubmitInterceptor;

    /**
     * 自定义拦截规则
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(repeatSubmitInterceptor).addPathPatterns("/**");
    }

    /**
     * 跨域配置
     */
    @Bean
    public CorsFilter corsFilter() {
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowCredentials(true);
        // 设置访问源地址
        config.addAllowedOriginPattern("*");
        // 设置访问源请求头
        config.addAllowedHeader("*");
        // 设置访问源请求方法
        config.addAllowedMethod("*");
        // 有效期 1800秒
        config.setMaxAge(1800L);
        // 添加映射路径,拦截一切请求
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", config);
        // 返回新的CorsFilter
        return new CorsFilter(source);
    }
}

🍣测试重复提交

@RestController
@RequestMapping("/test")
public class ReSubmitController {

    @GetMapping("/resubmit")
    @NotRepeatSubmit(interval = 5000)
    @ApiOperation(value = "", notes = "")
    public AjaxResult list(@RequestParam("id") String id) {
        return new AjaxResult();
    }
}
id : 1
{
  "msg": "不允许重复提交,请稍候再试!!!!",
  "code": 500
}

扫码_搜索联合传播样式-标准色版.png

📢💨如果文章对你有帮助【关注👍点赞❤️收藏⭐】