springboot 防止表单重复提交,已经设置过期时间60秒,将sessionid和url做为key,发现在60秒内重复提交表单还是存在多次成功,未阻止,这是为什么?

wangxi得 发布于 2020/06/14 00:14
阅读 3K+
收藏 6

【开源中国 APP 全新上线】“动弹” 回归、集成大模型对话、畅读技术报告”

if(StringUtils.isEmpty(key)){
    log.error(" key为空 ");
    throw new Exception("key为空");
}
//如果缓存中存在此key 视为重复提交
if(redisTemplate.opsForValue().get(key) == null){
    //不存在 放入redis 设置超时时间为2s
    redisTemplate.opsForValue().set(key,key,60, TimeUnit.SECONDS);

}else{
    log.error(" 重复提交 ");
    throw new Exception("重复提交");
}


log.info(" ===================业务动态代理成功请求提交了=========================== ");
加载中
0
北极心
北极心

引用来自“北极心”的评论

/**
 * 尝试获取分布式锁
 * @param lockKey  * @param requestId 请求标识
 * @param expireTime 超期时间
 * @return 是否获取成功
 */
public boolean tryGetDistributedLock(String lockKey, String requestId, int expireTime)throws Exception {
    RedisConnection conn = jedisConnectionFactory.getConnection();
    Jedis jedis = (Jedis) conn.getNativeConnection();
    String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);
    conn.close();
    if (LOCK_SUCCESS.equals(result)) {
        return true;
    }
    return false;

}

/**
 * 释放分布式锁
 * @param lockKey  * @param requestId 请求标识
 * @return 是否释放成功
 */
public boolean releaseDistributedLock(String lockKey, String requestId)throws Exception{
    RedisConnection conn = jedisConnectionFactory.getConnection();
    Jedis jedis = (Jedis) conn.getNativeConnection();
    String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
    Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));
    conn.close();
    if (RELEASE_SUCCESS.equals(result)) {
        return true;
    }
    return false;

}

 

try {
    // 3. 获取分布式锁
    while (true) {
        boolean isLock = jedisService.tryGetDistributedLock(distributeLockKeyOfAllocAgent, requestId, DISTRIBUTED_LOCK_EXPIRE_TIME);

        if (!isLock) {
            LOGGER.info("ServiceQuene||allotAgent||获取锁失败, 休眠{}ms. key:{}", SLEEP_TIME, distributeLockKeyOfAllocAgent);

            Thread.sleep(SLEEP_TIME);
            continue;
        }
        break;
    }

} catch (Exception ex) {
    LOGGER.error("ServiceQuene {}", agentUser);
} finally {
    jedisService.releaseDistributedLock(distributeLockKeyOfAllocAgent, requestId);
}

补充上面的字符串转表情了

2
Longer_JzL
Longer_JzL

“先取值后进行判断,再做处理”,这种做法,典型的不安全。会有并发问题。

redisTemplate.opsForValue().get(key) == null

这句是有并发问题的。

1
北极心
北极心
/**
 * 尝试获取分布式锁
 * @param lockKey  * @param requestId 请求标识
 * @param expireTime 超期时间
 * @return 是否获取成功
 */
public boolean tryGetDistributedLock(String lockKey, String requestId, int expireTime)throws Exception {
    RedisConnection conn = jedisConnectionFactory.getConnection();
    Jedis jedis = (Jedis) conn.getNativeConnection();
    String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);
    conn.close();
    if (LOCK_SUCCESS.equals(result)) {
        return true;
    }
    return false;

}

/**
 * 释放分布式锁
 * @param lockKey  * @param requestId 请求标识
 * @return 是否释放成功
 */
public boolean releaseDistributedLock(String lockKey, String requestId)throws Exception{
    RedisConnection conn = jedisConnectionFactory.getConnection();
    Jedis jedis = (Jedis) conn.getNativeConnection();
    String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
    Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));
    conn.close();
    if (RELEASE_SUCCESS.equals(result)) {
        return true;
    }
    return false;

}

 

try {
    // 3. 获取分布式锁
    while (true) {
        boolean isLock = jedisService.tryGetDistributedLock(distributeLockKeyOfAllocAgent, requestId, DISTRIBUTED_LOCK_EXPIRE_TIME);

        if (!isLock) {
            LOGGER.info("ServiceQuene||allotAgent||获取锁失败, 休眠{}ms. key:{}", SLEEP_TIME, distributeLockKeyOfAllocAgent);

            Thread.sleep(SLEEP_TIME);
            continue;
        }
        break;
    }

} catch (Exception ex) {
    LOGGER.error("ServiceQuene {}", agentUser);
} finally {
    jedisService.releaseDistributedLock(distributeLockKeyOfAllocAgent, requestId);
}
0
GreysonYee
GreysonYee

防止重复提交,弄个“加载动画”吧。服务器没影响结果,就不允许再次执行提交操作。

wangxi得
wangxi得
loading已经做了,很奇怪的现象,手动快速重复点击按钮,发现存在多次controller层调用service层成功,但是通过模拟多线程请求就没有这个问题,只有一给成功,其他都是重复提交,不知道你们开发有没有遇到这种现象?
0
也许____
也许____

看看日志,打印重复提交没有,key的判断逻辑没什么问题

wangxi得
wangxi得
loading已经做了,很奇怪的现象,手动快速重复点击按钮,发现存在多次controller层调用service层成功,但是通过模拟多线程请求就没有这个问题,只有一给成功,其他都是重复提交,不知道你们开发有没有遇到这种现象
0
LeoXu1990
LeoXu1990

http request是并发发送的. 会不会是第一个request 发送到服务器, 服务器还没来得及处理.(收到请求但是redis还没写入进去)

用户快速点击第二次, 这时候服务器可能会拥堵等等, 检查重复的时候redis还没第一次写入的数据 于是便处理了.

 

0
f
freezingsky

setNx,年轻人

0
胡萝卜炒肉
胡萝卜炒肉

加锁

0
YYXX007
YYXX007

图简单的话redis那一块加个锁万事大吉

0
Mi_米
Mi_米
该评论暂时无法显示,详情咨询 QQ 群:点此入群
OSCHINA
登录后可查看更多优质内容
返回顶部
顶部