java数据进行验证,防重复提交

本文介绍了防止数据重复提交的三种策略:1) 比较修改时间戳,相同则允许操作;2) 使用令牌(token)进行会话验证,提交时比对session中token;3) 应用Redis分布式锁实现通用的并发控制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.在进入页面的时候提交修改时间,然后进行提交的时候,将修改时间带入到后台,然后查询数据库,当两个时间相同的时候,进行操作,否则就提示已操作.
2.看到一种更广泛的方式,就是在进入页面的时候生成一个token,然后赋值到session中,同时带入到页面中,然后提交过程中,将页面带入的token与session中的token比较,相同则通过校验并删除session。
3、使用redis分布式锁,这边写了一个通用方法,只需要标注下CustomerLock就可以了

	@RequestMapping("test")
    @CustomerLock(key="#id",lockTimeOut = 3000)
    public void test(@Param("id") @RequiredParam Long id) {
       
    }
    @RequestMapping("test")
    @CustomerLock(key="#testVo.id",lockTimeOut = 3000)
    public void test(TestVo testVo) {

    }

首先定义一个CustomerLock注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomerLock {
 
    String key() ;
    long lockTimeOut() default 9000 ;
    String errorMessage() default "请勿重复提交";
}

然后定义一个RedisLock

@Component
@Slf4j
public class RedisLock {
 
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
 
    /**
     * 加锁
     * @param key
     * @param value 当前时间+超时时间
     * @return
     */
    public boolean lock(String key, String value) {
        if(redisTemplate.opsForValue().setIfAbsent(key, value)) {
            return true;
        }
        //currentValue=A   这两个线程的value都是B  其中一个线程拿到锁
        String currentValue = redisTemplate.opsForValue().get(key);
        //如果锁过期
        if (!StringUtils.isEmpty(currentValue)
                && Long.parseLong(currentValue) < System.currentTimeMillis()) {
            //获取上一个锁的时间
            String oldValue = redisTemplate.opsForValue().getAndSet(key, value);
            if (!StringUtils.isEmpty(oldValue) && oldValue.equals(currentValue)) {
                return true;
            }
        }
 
        return false;
    }
 
    /**
     * 解锁
     * @param key
     * @param value
     */
    public void unlock(String key, String value) {
        try {
            String currentValue = redisTemplate.opsForValue().get(key);
            if (!StringUtils.isEmpty(currentValue) && currentValue.equals(value)) {
                redisTemplate.opsForValue().getOperations().delete(key);
            }
        }catch (Exception e) {
            log.error("【redis分布式锁】解锁异常, {}", e);
        }
    }
 
}

如果是springmvc项目则可以使用如下aop接入

@Aspect
@Component
public class CustomerLockAspect {
    @Autowired
    private RedisLock lock;
    private ExpressionEvaluator <String> evaluator = new ExpressionEvaluator<>();
    @Around("@annotation(customerLock)")
    public Object invokeWithRedisLock(ProceedingJoinPoint pjp, CustomerLock customerLock) throws Throwable {
        String methodName = getMethodName(pjp);
        String key = getAttachmentId(pjp,customerLock);
        String lockName ="reception_boss:"+methodName +":"+key;
        long expires = System.currentTimeMillis() + customerLock.lockTimeOut();
        try {
            Boolean flag = lock.lock(lockName,String.valueOf(expires));
            if(flag){
                return pjp.proceed();
            }else{
                return WebResult.fail("500",customerLock.errorMessage());
            }
        } finally {
            lock.unlock(lockName,String.valueOf(expires));
        }
    }
    private String getMethodName(ProceedingJoinPoint pjp) {
        String methodName= pjp.getTarget().getClass().getName()+":"+((MethodSignature) pjp.getSignature()).getMethod().getName();
        return methodName;
    }
 
    private String getAttachmentId(JoinPoint joinPoint, CustomerLock customerLock) {
        if (joinPoint.getArgs() == null) {
            return null;
        }
        EvaluationContext evaluationContext = evaluator.createEvaluationContext(joinPoint.getTarget(), joinPoint.getTarget().getClass(), ((MethodSignature) joinPoint.getSignature()).getMethod(), joinPoint.getArgs());
        AnnotatedElementKey methodKey = new AnnotatedElementKey(((MethodSignature) joinPoint.getSignature()).getMethod(), joinPoint.getTarget().getClass());
        return evaluator.condition(customerLock.key(), methodKey, evaluationContext, String.class);
    }
}
public class ExpressionEvaluator<T> extends CachedExpressionEvaluator {
    
    
    private final ParameterNameDiscoverer discoverer = new DefaultParameterNameDiscoverer();
    private final Map<AnnotatedElementKey, Method> methodCache = new ConcurrentHashMap<>(128);
    private final Map<ExpressionKey, Expression> conditionCache = new ConcurrentHashMap<>(128);


    public T condition(String conditionExpression, AnnotatedElementKey elementKey, EvaluationContext evalContext, Class<T> clazz) {
        return getExpression(this.conditionCache, elementKey, conditionExpression).getValue(evalContext, clazz);
    }

    public EvaluationContext createEvaluationContext(Object object, Class<?> targetClass, Method method, Object[] args) {
        Method targetMethod = getTargetMethod(targetClass, method);
        RootObject root = new RootObject(object, args);
        return new MethodBasedEvaluationContext(root, targetMethod, args, this.discoverer);
    }

    private Method getTargetMethod(Class<?> targetClass, Method method) {
        AnnotatedElementKey methodKey = new AnnotatedElementKey(method, targetClass);
        Method targetMethod = this.methodCache.get(methodKey);
        if (targetMethod == null) {
            targetMethod = AopUtils.getMostSpecificMethod(method, targetClass);
            if (targetMethod == null) {
                targetMethod = method;
            }
            this.methodCache.put(methodKey, targetMethod);
        }
        return targetMethod;
    }
    class RootObject {
        private final Object object;

        private final Object[] args;

        public RootObject(Object object, Object[] args) {
            this.object = object;
            this.args = args;
        }

        public Object getobject() {
            return object;
        }

        public Object[] getArgs() {
            return args;
        }
    }
   
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值