当用户在新增一条数据时,快速点击多次提交按钮,若没有做拦截,那么将会导致新增多条无意义的数据
本文参考其他文章编写:原文地址1 ,原文地址2
解决方案:
- 前端点击提交后将按钮禁用
document.getElementById("btn").disabled = true;
注 :无法避免恶意用户调用接口提交数据,比如直接请求接口,并不通过前端页面
2.在Java代码增加synchronized关键字使提交数据一条一条执行
public synchronized Result<?> save (Model model){ return null }
注 :使用起来比较简单但性能很低,在处理数据时会有排队等候的问题,并且在多实例部署时无法起到作用 3.使用Redis结合拦截器进行拦截 参考文章
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
import java.lang.annotation.*;
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface AvoidReSubmit {
long expireTime() default 30 * 1000L;
}
import com.hk.frame.util.AvoidReSubmit;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.concurrent.TimeUnit;
@Aspect
@Component
public class AvoidReSubmitAspect {
@Resource
private RedisTemplate redisTemplate;
@Pointcut(value = "@annotation(com.hk.frame.util.AvoidReSubmit)")
public void submit() {
}
@Before("submit()&&@annotation(avoidReSubmit)")
public void doBefore(JoinPoint joinPoint, AvoidReSubmit avoidReSubmit) {
StringBuffer sb = new StringBuffer();
for(Object object : joinPoint.getArgs()){
sb.append(object);
}
String key = md5(sb.toString());
long expireTime = avoidReSubmit.expireTime();
ValueOperations valueOperations = redisTemplate.opsForValue();
Object object = valueOperations.get(key);
if(null != object){
throw new RuntimeException("您已经提交了请求,请不要重复提交哦!");
}
valueOperations.set(key, "1", expireTime, TimeUnit.MILLISECONDS);
}
@Around("submit()&&@annotation(avoidReSubmit)")
public Object doAround(ProceedingJoinPoint proceedingJoinPoint, AvoidReSubmit avoidReSubmit) throws Throwable {
System.out.println("环绕通知:");
Object result = null;
result = proceedingJoinPoint.proceed();
return result;
}
@After("submit()")
public void doAfter() {
System.out.println("******拦截后的逻辑******");
}
private String md5(String str){
if (str == null || str.length() == 0) {
throw new IllegalArgumentException("String to encript cannot be null or zero length");
}
StringBuffer hexString = new StringBuffer();
try {
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(str.getBytes());
byte[] hash = md.digest();
for (int i = 0; i < hash.length; i++) {
if ((0xff & hash[i]) < 0x10) {
hexString.append("0" + Integer.toHexString((0xFF & hash[i])));
} else {
hexString.append(Integer.toHexString(0xFF & hash[i]));
}
}
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return hexString.toString();
}
}
@ResponseBody
@RequestMapping(value = "/add", method = RequestMethod.POST, produces = "application/json;charset=UTF-8")
@AvoidReSubmit(expireTime = 1000 * 3)
public void addShoppingGuide(@RequestBody Map<String,Object> param) {
return coas.add(param);
}
4.通过数据库唯一约束进行限制,再从代码中捕捉异常
ALTER TABLE 表名 ADD unique(列名);
适用于,比如user表中只希望userName一样时阻止新增
ALTER TABLE <表名> ADD UNIQUE KEY(列名1,列名2);
适用于,比如user表中,希望当userName和age都一样时阻止新增
补充: mysql 查询唯一索引
show index from 表名;
mysql删除唯一索引
alter table 表名 drop index 索引名称;
|