令牌验证¶
在确认订单操作和提交订单操作中,要分别生成一个 token 令牌、 校验 token 令牌,保证幂等性。
生成并保存令牌¶
Token = 前缀 + 业务对象ID + UUID
// UUID作为令牌
String token = UUID.randomUUID().toString().replace("-", "");
// 在业务对象中保存 token
orderConfirmVo.setOrderToken(token);
// 使用 redis 保存 token,并设置过期时间:30分钟
stringRedisTemplate.opsForValue().set(OrderConstant.USER_ORDER_TOKEN_PREFIX + memberRespVo.getId(), token, 30, TimeUnit.MINUTES);
验证令牌¶
对令牌的对比和删除必须保证原子性。
- 从 redis 和业务对象中分别获取 token
String redisOrderToken = stringRedisTemplate.opsForValue().get(OrderConstant.USER_ORDER_TOKEN_PREFIX + memberRespVo.getId());
String orderToken = orderSubmitVo.getOrderToken();
- Lua 脚本保证原子性
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
- 执行脚本,进行对比
Long result = stringRedisTemplate.execute(new DefaultRedisScript<Long>(script, Long.class),
Arrays.asList(OrderConstant.USER_ORDER_TOKEN_PREFIX + memberRespVo.getId()), orderToken);
返回 0 代表失败, 1 代表成功。