赞
踩
// 通过传入的key获取到的value与参数avg进行对比,一致说明数据存在reids中,删除key的数据,
// 不一致说明是重复提交,返回0后续业务处理。
if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
/**
* Redis配置类
* @author 单程车票
*/
@Configuration
public class RedisConfig {
/**
* 配置Redis的序列化器
*/
public RedisTemplate<Object,Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<Object,Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
// 使用jackson序列化方式序列化json
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
}
# 端口号
server:
port: 8888
# 配置Redis
spring:
redis:
host: localhost
port: 6379
database: 0
/**
* 业务接口类
* @author 单程车票
*/
public interface TokenService {
// 创建token
String getToken();
// 校验token并执行业务代码
boolean checkToken(HttpServletRequest request);
}
/**
* 主要业务
* @author 单程车票
*/
@Service
public class TokenServiceImpl implements TokenService {
@Resource
private StringRedisTemplate stringRedisTemplate;
@Override
public String getToken() {
// 通过UUID生成token
String token = UUID.randomUUID().toString().replaceAll("-", "");
// 将生成的token存入redis中,这里为了方便观察,把key写死为testToken,因为只测试一个接口,实际开发中,key是需要动态的(可以找一个唯一值替代,比如订单号等)
stringRedisTemplate.opsForValue().set("testToken", token);
// 返回token
return token;
}
@Override
public boolean checkToken(HttpServletRequest request) {
// 从请求头中获取token
String token = request.getHeader("token");
// 判断是否为空
if (StringUtils.isEmpty(token)) {
// token为空则抛出异常
throw new CustomException("请求头未携带token!");
}
// LUA脚本保证获取token,校验token,删除token是原子性的
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
// 调用execute执行,第一个参数传入脚本,第二个参数传入key(这里是之前写死的testToken),第三个参数传入AVG(这里是请求头携带的token)
// 该lua脚本会对比 传入的key获取到的value是否与请求头获取的token一致,一致说明存在,删除key的数据,不一致返回0
Long res = stringRedisTemplate.execute(new DefaultRedisScript<>(script, Long.class), List.of("testToken"), token);
// 不一致,说明不存在,抛出重复异常
if (res == 0) {
throw new CustomException("请求重复提交!");
}
// 执行业务代码
System.out.println("执行业务代码");
return true;
}
}
/**
* 测试
* @author 单程车票
*/
@RestController
public class OrderController {
@Autowired
private TokenService tokenService;
@GetMapping("/getToken")
public R<String> getToken() {
String token = tokenService.getToken();
return R.success(token);
}
@PostMapping("/order")
public R<String> order(HttpServletRequest request) {
// 校验并执行业务代码
boolean res = tokenService.checkToken(request);
if (res) return R.success("执行成功!");
else return R.fail("执行失败");
}
}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。