java实现防止重复提交
防止重复提交是一种常见的安全措施,用于防止用户在短时间内多次提交相同的请求。通过使用注解,我们可以在代码层面实现这种防护机制,以提高系统的安全性和稳定性。
防止重复提交的机制在许多Web应用程序中都是必需的,特别是在涉及到敏感操作或需要保证数据一致性的情况下。以下是一些常见的场景:
- 提交表单:防止用户多次提交表单,避免重复插入或修改数据。
- 支付请求:防止用户多次点击支付按钮,避免重复扣款。
- 并发操作:防止多个线程同时执行相同的操作,避免数据冲突。
除了使用注解,还有其他一些方式可以实现防止重复提交的机制,例如:
- Token验证:在每个请求中生成一个唯一的令牌,并将其存储在会话或隐藏表单字段中。服务器在处理请求时验证令牌的唯一性,如果重复提交相同的令牌,则拒绝处理。
- 重定向:在处理请求后,将用户重定向到一个结果页面,而不是直接返回结果。这样,用户无法通过刷新页面或再次提交相同的请求。
- 前端验证:在前端使用JavaScript等技术验证用户输入,避免重复提交无效的请求。
下面是使用注解实现防止重复提交的一般步骤:
- 定义注解:创建一个自定义注解,用于标记需要进行重复提交检查的方法或接口。
- 实现拦截器或切面:创建一个拦截器或切面,用于在方法执行前进行重复提交检查。如下是实现一个拦截器
/**
* 验证是否重复提交
* @param request
* @return
* @throws Exception
*/
️public ️abstract ️boolean ️isRepeatSubmit(HttpServletRequest request, RepeatSubmit annotation);
定义一个实现类,继承拦截器这个类,实现其方法具体的验证方法也在其中
类中的具体方法如下
️@Override
️public ️boolean ️isRepeatSubmit(HttpServletRequest request, RepeatSubmit annotation)
{
String nowParams = "";
️if (request ️instanceof RepeatedlyRequestWrapper)
{
RepeatedlyRequestWrapper repeatedlyRequest = (RepeatedlyRequestWrapper) request;
nowParams = HttpHelper.getBodyString(repeatedlyRequest);
}
// body参数为空,获取Parameter的数据
️if (StringUtils.isEmpty(nowParams))
{
nowParams = JSONObject.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(header));
// 唯一标识(指定key + url + 消息头)
String cacheRepeatKey = Constants.REPEAT_SUBMIT_KEY + url + submitKey;
Object sessionObj = redisCache.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, annotation.interval()))
{
️return ️true;
}
}
}
Map<String, Object> cacheMap = ️new HashMap<String, Object>();
cacheMap.put(url, nowDataMap);
redisCache.setCacheObject(cacheRepeatKey, cacheMap, annotation.interval(), TimeUnit.MILLISECONDS);
️return ️false;
}
其中compareParams是判断参数是否相同方法,compareTime判断两次间隔时间
- 使用注解:在需要进行重复提交检查的方法或接口上添加自定义注解。
️@RestController
️public ️class ️TestController {
️@RepeatSubmit
️@PostMapping("/test")
️public ️void ️test(@RequestBody User user) {
// 处理逻辑
}
}
在上述示例中,我们定义了一个AvoidDuplicateSubmit注解,并在UserController的createUser方法上使用了该注解。然后,我们通过AvoidDuplicateSubmitAspect切面类,在方法执行前进行重复提交检查。
总结通过使用注解,我们可以在代码层面实现防止重复提交的机制。通过定义自定义注解、实现拦截器或切面以及在需要进行重复提交检查的方法上使用注解,我们可以有效地防止用户在短时间内多次提交相同的请求,提高系统的安全性和稳定性。