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

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

上级 45c5bc7c
...@@ -5,7 +5,9 @@ import com.kx.utils.idempotent.TokenCache; ...@@ -5,7 +5,9 @@ import com.kx.utils.idempotent.TokenCache;
import com.kx.utils.idempotent.TokenValueRequestHolder; import com.kx.utils.idempotent.TokenValueRequestHolder;
import com.kx.utils.idempotent.impl.DefaultTokenValueRequestHolder; import com.kx.utils.idempotent.impl.DefaultTokenValueRequestHolder;
import com.kx.utils.idempotent.impl.InMemoryTokenCache; import com.kx.utils.idempotent.impl.InMemoryTokenCache;
import com.kx.utils.idempotent.impl.RedisTokenCache;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.ComponentScan;
...@@ -15,11 +17,12 @@ import org.springframework.stereotype.Component; ...@@ -15,11 +17,12 @@ import org.springframework.stereotype.Component;
* 幂等工具自动配置类 * 幂等工具自动配置类
* @author kongxiang * @author kongxiang
*/ */
@ComponentScan(Const.BASE_PACKAGE+".idempotent") @ComponentScan(Const.BASE_PACKAGE + ".idempotent")
@AutoConfigureAfter(RedisAutoConfiguration.class)
@Slf4j @Slf4j
public class IdempotentAutoConfiguration { public class IdempotentAutoConfiguration {
@Bean @Bean
@ConditionalOnMissingBean(TokenCache.class) @ConditionalOnMissingBean({RedisTokenCache.class})
public InMemoryTokenCache inMemoryTokenCache(){ public InMemoryTokenCache inMemoryTokenCache(){
log.info("加载默认TokenCache: {}",InMemoryTokenCache.class); log.info("加载默认TokenCache: {}",InMemoryTokenCache.class);
return new InMemoryTokenCache(); return new InMemoryTokenCache();
......
package com.kx.config; 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.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisOperations; import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
/** /**
* redis引入,工具类使用redis相关的配置 * redis引入,工具类使用redis相关的配置
...@@ -11,5 +16,13 @@ import org.springframework.data.redis.core.RedisOperations; ...@@ -11,5 +16,13 @@ import org.springframework.data.redis.core.RedisOperations;
*/ */
@Configuration(proxyBeanMethods = false) @Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RedisOperations.class) @ConditionalOnClass(RedisOperations.class)
@AutoConfigureAfter(org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration.class)
public class RedisAutoConfiguration { 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.
先完成此消息的编辑!
想要评论请 注册