提交 0ea5bb1d 编写于 作者: 无难事者若执's avatar 无难事者若执

fix : 【幂等】 :实现基于Redis缓存的防重令牌幂等算法

上级 45c5bc7c
......@@ -5,7 +5,9 @@ import com.kx.utils.idempotent.TokenCache;
import com.kx.utils.idempotent.TokenValueRequestHolder;
import com.kx.utils.idempotent.impl.DefaultTokenValueRequestHolder;
import com.kx.utils.idempotent.impl.InMemoryTokenCache;
import com.kx.utils.idempotent.impl.RedisTokenCache;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
......@@ -15,11 +17,12 @@ import org.springframework.stereotype.Component;
* 幂等工具自动配置类
* @author kongxiang
*/
@ComponentScan(Const.BASE_PACKAGE+".idempotent")
@ComponentScan(Const.BASE_PACKAGE + ".idempotent")
@AutoConfigureAfter(RedisAutoConfiguration.class)
@Slf4j
public class IdempotentAutoConfiguration {
@Bean
@ConditionalOnMissingBean(TokenCache.class)
@ConditionalOnMissingBean({RedisTokenCache.class})
public InMemoryTokenCache inMemoryTokenCache(){
log.info("加载默认TokenCache: {}",InMemoryTokenCache.class);
return new InMemoryTokenCache();
......
package com.kx.config;
import com.kx.utils.idempotent.impl.RedisTokenCache;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
/**
* redis引入,工具类使用redis相关的配置
......@@ -11,5 +16,13 @@ import org.springframework.data.redis.core.RedisOperations;
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RedisOperations.class)
@AutoConfigureAfter(org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration.class)
public class RedisAutoConfiguration {
@Bean
@ConditionalOnBean(RedisOperations.class)
public RedisTokenCache redisTokenCache(StringRedisTemplate stringRedisTemplate) {
return new RedisTokenCache(stringRedisTemplate);
}
}
package com.kx.utils.idempotent.impl;
import com.kx.utils.idempotent.TokenCache;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;
/**
* redis 实现的token缓存
*
* @author kongxiang
*/
public class RedisTokenCache implements TokenCache {
/**
* 存入 Redis 的 Token 键的前缀
*/
private static final String IDEMPOTENT_TOKEN_PREFIX = "idempotent_token:";
private RedisOperations redisOperations;
public RedisTokenCache(RedisOperations redisOperations) {
this.redisOperations = redisOperations;
}
@Override
public boolean putToken(String token, String value) {
try {
String key = IDEMPOTENT_TOKEN_PREFIX + token;
redisOperations.opsForValue().set(key, value, 10, TimeUnit.MINUTES);
} catch (Exception ex) {
ex.printStackTrace();
return false;
}
return true;
}
@Override
public boolean validToken(String token, String value) {
// 设置 Lua 脚本,其中 KEYS[1] 是 key,KEYS[2] 是 value
String script = "if redis.call('get', KEYS[1]) == KEYS[2] then return redis.call('del', KEYS[1]) else return 0 end";
RedisScript redisScript = new DefaultRedisScript<>(script, Long.class);
// 根据 Key 前缀拼接 Key
String key = IDEMPOTENT_TOKEN_PREFIX + token;
// 执行 Lua 脚本
Long result = (long) redisOperations.execute(redisScript, Arrays.asList(key, value));
// 根据返回结果判断是否成功成功匹配并删除 Redis 键值对,若果结果不为空和0,则验证通过
if (result != null && result != 0L) {
//log.info("验证 token={},key={},value={} 成功", token, key, value);
return true;
}
//log.info("验证 token={},key={},value={} 失败", token, key, value);
return false;
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册