🧑🎓 个人主页:花棉袄
📖 本章内容:【自定义注解-防止重复提交】
🌼SpringBoot:搭建后端项目👉 Gitee仓库地址
🥗Redis配置
- 🌳掘金文档:👉🏽SpringBoot-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
}