From 44ecbe784590ac7045e317a9c1f829ea05f97478 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90li?= <15040126243@163.com> Date: Tue, 3 Jan 2023 13:20:20 +0800 Subject: [PATCH 1/3] =?UTF-8?q?add=20=E5=A2=9E=E5=8A=A0=20=E9=80=82?= =?UTF-8?q?=E9=85=8D=20redisson=20=E5=AE=A2=E6=88=B7=E7=AB=AF=20jackson=20?= =?UTF-8?q?=E5=BA=8F=E5=88=97=E5=8C=96=20=E6=8F=92=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sa-token-dependencies/pom.xml | 15 + sa-token-plugin/pom.xml | 1 + .../sa-token-dao-redisson-jackson/.gitignore | 13 + .../sa-token-dao-redisson-jackson/pom.xml | 48 +++ .../dao/SaSessionForJacksonCustomized.java | 32 ++ .../dao/SaTokenDaoRedissonJackson.java | 283 ++++++++++++++++++ .../main/resources/META-INF/spring.factories | 1 + 7 files changed, 393 insertions(+) create mode 100644 sa-token-plugin/sa-token-dao-redisson-jackson/.gitignore create mode 100644 sa-token-plugin/sa-token-dao-redisson-jackson/pom.xml create mode 100644 sa-token-plugin/sa-token-dao-redisson-jackson/src/main/java/cn/dev33/satoken/dao/SaSessionForJacksonCustomized.java create mode 100644 sa-token-plugin/sa-token-dao-redisson-jackson/src/main/java/cn/dev33/satoken/dao/SaTokenDaoRedissonJackson.java create mode 100644 sa-token-plugin/sa-token-dao-redisson-jackson/src/main/resources/META-INF/spring.factories diff --git a/sa-token-dependencies/pom.xml b/sa-token-dependencies/pom.xml index f172bbd8..ae1dbbdf 100644 --- a/sa-token-dependencies/pom.xml +++ b/sa-token-dependencies/pom.xml @@ -32,6 +32,7 @@ 0.9.1 1.2.83 2.0.15 + 3.19.0 @@ -120,6 +121,13 @@ spring-boot-starter-data-redis ${springboot.version} + + + + org.redisson + redisson-spring-boot-starter + ${redisson.version} + @@ -183,6 +191,13 @@ spring-boot-starter-aop ${springboot.version} + + + + org.springframework.boot + spring-boot-starter-actuator + ${springboot.version} + diff --git a/sa-token-plugin/pom.xml b/sa-token-plugin/pom.xml index 71e22215..80213083 100644 --- a/sa-token-plugin/pom.xml +++ b/sa-token-plugin/pom.xml @@ -22,6 +22,7 @@ sa-token-dao-redis-jackson sa-token-dao-redis-fastjson sa-token-dao-redis-fastjson2 + sa-token-dao-redisson-jackson sa-token-dao-redisx sa-token-alone-redis sa-token-dialect-thymeleaf diff --git a/sa-token-plugin/sa-token-dao-redisson-jackson/.gitignore b/sa-token-plugin/sa-token-dao-redisson-jackson/.gitignore new file mode 100644 index 00000000..8122f47c --- /dev/null +++ b/sa-token-plugin/sa-token-dao-redisson-jackson/.gitignore @@ -0,0 +1,13 @@ +target/ + +node_modules/ +bin/ +.settings/ +unpackage/ +.classpath +.project + +.factorypath + +.idea/ +.iml \ No newline at end of file diff --git a/sa-token-plugin/sa-token-dao-redisson-jackson/pom.xml b/sa-token-plugin/sa-token-dao-redisson-jackson/pom.xml new file mode 100644 index 00000000..568c020e --- /dev/null +++ b/sa-token-plugin/sa-token-dao-redisson-jackson/pom.xml @@ -0,0 +1,48 @@ + + + 4.0.0 + + + cn.dev33 + sa-token-plugin + ${revision} + ../pom.xml + + jar + + sa-token-dao-redisson-jackson + sa-token-dao-redisson-jackson + sa-token integrate redisson (to jackson) + + + + + cn.dev33 + sa-token-core + + + + org.redisson + redisson-spring-boot-starter + + + + + com.fasterxml.jackson.core + jackson-databind + true + + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + true + + + + + + + diff --git a/sa-token-plugin/sa-token-dao-redisson-jackson/src/main/java/cn/dev33/satoken/dao/SaSessionForJacksonCustomized.java b/sa-token-plugin/sa-token-dao-redisson-jackson/src/main/java/cn/dev33/satoken/dao/SaSessionForJacksonCustomized.java new file mode 100644 index 00000000..d4505508 --- /dev/null +++ b/sa-token-plugin/sa-token-dao-redisson-jackson/src/main/java/cn/dev33/satoken/dao/SaSessionForJacksonCustomized.java @@ -0,0 +1,32 @@ +package cn.dev33.satoken.dao; + +import cn.dev33.satoken.session.SaSession; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + +/** + * Jackson定制版SaSession,忽略 timeout 等属性的序列化 + * + * @author kong + * + */ +@JsonIgnoreProperties({"timeout"}) +public class SaSessionForJacksonCustomized extends SaSession { + + /** + * + */ + private static final long serialVersionUID = -7600983549653130681L; + + public SaSessionForJacksonCustomized() { + super(); + } + + /** + * 构建一个Session对象 + * @param id Session的id + */ + public SaSessionForJacksonCustomized(String id) { + super(id); + } + +} diff --git a/sa-token-plugin/sa-token-dao-redisson-jackson/src/main/java/cn/dev33/satoken/dao/SaTokenDaoRedissonJackson.java b/sa-token-plugin/sa-token-dao-redisson-jackson/src/main/java/cn/dev33/satoken/dao/SaTokenDaoRedissonJackson.java new file mode 100644 index 00000000..b0ea9d45 --- /dev/null +++ b/sa-token-plugin/sa-token-dao-redisson-jackson/src/main/java/cn/dev33/satoken/dao/SaTokenDaoRedissonJackson.java @@ -0,0 +1,283 @@ +package cn.dev33.satoken.dao; + +import cn.dev33.satoken.strategy.SaStrategy; +import cn.dev33.satoken.util.SaFoxUtil; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer; +import org.redisson.api.RBatch; +import org.redisson.api.RBucket; +import org.redisson.api.RBucketAsync; +import org.redisson.api.RedissonClient; +import org.redisson.codec.JsonJacksonCodec; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; +import org.springframework.data.redis.serializer.StringRedisSerializer; +import org.springframework.stereotype.Component; + +import java.lang.reflect.Field; +import java.time.Duration; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * Sa-Token 持久层实现 [Redisson客户端、Redis存储、Jackson序列化] + * + * @author 疯狂的狮子Li + * + */ +@Component +public class SaTokenDaoRedissonJackson implements SaTokenDao { + + public static final String DATE_TIME_PATTERN = "yyyy-MM-dd HH:mm:ss"; + public static final String DATE_PATTERN = "yyyy-MM-dd"; + public static final String TIME_PATTERN = "HH:mm:ss"; + public static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern(DATE_TIME_PATTERN); + public static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern(DATE_PATTERN); + public static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern(TIME_PATTERN); + + /** + * ObjectMapper对象 (以public作用域暴露出此对象,方便开发者二次更改配置) + */ + public ObjectMapper objectMapper; + + /** + * redisson 客户端 + */ + public RedissonClient redissonClient; + + /** + * 标记:是否已初始化成功 + */ + public boolean isInit; + + @Autowired + public void init(RedissonClient redissonClient) { + // 不重复初始化 + if(this.isInit) { + return; + } + + // 指定相应的序列化方案 + GenericJackson2JsonRedisSerializer valueSerializer = new GenericJackson2JsonRedisSerializer(); + // 通过反射获取Mapper对象, 增加一些配置, 增强兼容性 + try { + Field field = GenericJackson2JsonRedisSerializer.class.getDeclaredField("mapper"); + field.setAccessible(true); + ObjectMapper objectMapper = (ObjectMapper) field.get(valueSerializer); + this.objectMapper = objectMapper; + // 配置[忽略未知字段] + this.objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + // 配置[时间类型转换] + JavaTimeModule timeModule = new JavaTimeModule(); + // LocalDateTime序列化与反序列化 + timeModule.addSerializer(new LocalDateTimeSerializer(DATE_TIME_FORMATTER)); + timeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DATE_TIME_FORMATTER)); + // LocalDate序列化与反序列化 + timeModule.addSerializer(new LocalDateSerializer(DATE_FORMATTER)); + timeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DATE_FORMATTER)); + // LocalTime序列化与反序列化 + timeModule.addSerializer(new LocalTimeSerializer(TIME_FORMATTER)); + timeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(TIME_FORMATTER)); + this.objectMapper.registerModule(timeModule); + // 重写 SaSession 生成策略 + SaStrategy.me.createSession = (sessionId) -> new SaSessionForJacksonCustomized(sessionId); + } catch (Exception e) { + System.err.println(e.getMessage()); + } + + // 开始初始化相关组件 + redissonClient.getConfig().setCodec(new JsonJacksonCodec(objectMapper)); + this.redissonClient = redissonClient; + this.isInit = true; + } + + + /** + * 获取Value,如无返空 + */ + @Override + public String get(String key) { + RBucket rBucket = redissonClient.getBucket(key); + return rBucket.get(); + } + + /** + * 写入Value,并设定存活时间 (单位: 秒) + */ + @Override + public void set(String key, String value, long timeout) { + if(timeout == 0 || timeout <= SaTokenDao.NOT_VALUE_EXPIRE) { + return; + } + // 判断是否为永不过期 + if(timeout == SaTokenDao.NEVER_EXPIRE) { + RBucket bucket = redissonClient.getBucket(key); + bucket.set(value); + } else { + RBatch batch = redissonClient.createBatch(); + RBucketAsync bucket = batch.getBucket(key); + bucket.setAsync(value); + bucket.expireAsync(Duration.ofSeconds(timeout)); + batch.execute(); + } + } + + /** + * 修修改指定key-value键值对 (过期时间不变) + */ + @Override + public void update(String key, String value) { + long expire = getTimeout(key); + // -2 = 无此键 + if(expire == SaTokenDao.NOT_VALUE_EXPIRE) { + return; + } + this.set(key, value, expire); + } + + /** + * 删除Value + */ + @Override + public void delete(String key) { + redissonClient.getBucket(key).delete(); + } + + /** + * 获取Value的剩余存活时间 (单位: 秒) + */ + @Override + public long getTimeout(String key) { + RBucket rBucket = redissonClient.getBucket(key); + long timeout = rBucket.remainTimeToLive(); + return timeout < 0 ? timeout : timeout / 1000; + } + + /** + * 修改Value的剩余存活时间 (单位: 秒) + */ + @Override + public void updateTimeout(String key, long timeout) { + // 判断是否想要设置为永久 + if(timeout == SaTokenDao.NEVER_EXPIRE) { + long expire = getTimeout(key); + if(expire == SaTokenDao.NEVER_EXPIRE) { + // 如果其已经被设置为永久,则不作任何处理 + } else { + // 如果尚未被设置为永久,那么再次set一次 + this.set(key, this.get(key), timeout); + } + return; + } + RBucket rBucket = redissonClient.getBucket(key); + rBucket.expire(Duration.ofSeconds(timeout)); + } + + + + /** + * 获取Object,如无返空 + */ + @Override + public Object getObject(String key) { + RBucket rBucket = redissonClient.getBucket(key); + return rBucket.get(); + } + + /** + * 写入Object,并设定存活时间 (单位: 秒) + */ + @Override + public void setObject(String key, Object object, long timeout) { + if(timeout == 0 || timeout <= SaTokenDao.NOT_VALUE_EXPIRE) { + return; + } + // 判断是否为永不过期 + if(timeout == SaTokenDao.NEVER_EXPIRE) { + RBucket bucket = redissonClient.getBucket(key); + bucket.set(object); + } else { + RBatch batch = redissonClient.createBatch(); + RBucketAsync bucket = batch.getBucket(key); + bucket.setAsync(object); + bucket.expireAsync(Duration.ofSeconds(timeout)); + batch.execute(); + } + + } + + /** + * 更新Object (过期时间不变) + */ + @Override + public void updateObject(String key, Object object) { + long expire = getObjectTimeout(key); + // -2 = 无此键 + if(expire == SaTokenDao.NOT_VALUE_EXPIRE) { + return; + } + this.setObject(key, object, expire); + } + + /** + * 删除Object + */ + @Override + public void deleteObject(String key) { + redissonClient.getBucket(key).delete(); + } + + /** + * 获取Object的剩余存活时间 (单位: 秒) + */ + @Override + public long getObjectTimeout(String key) { + RBucket rBucket = redissonClient.getBucket(key); + long timeout = rBucket.remainTimeToLive(); + return timeout < 0 ? timeout : timeout / 1000; + } + + /** + * 修改Object的剩余存活时间 (单位: 秒) + */ + @Override + public void updateObjectTimeout(String key, long timeout) { + // 判断是否想要设置为永久 + if(timeout == SaTokenDao.NEVER_EXPIRE) { + long expire = getObjectTimeout(key); + if(expire == SaTokenDao.NEVER_EXPIRE) { + // 如果其已经被设置为永久,则不作任何处理 + } else { + // 如果尚未被设置为永久,那么再次set一次 + this.setObject(key, this.getObject(key), timeout); + } + return; + } + RBucket rBucket = redissonClient.getBucket(key); + rBucket.expire(Duration.ofSeconds(timeout)); + } + + + /** + * 搜索数据 + */ + @Override + public List searchData(String prefix, String keyword, int start, int size, boolean sortType) { + Stream stream = redissonClient.getKeys().getKeysStreamByPattern(prefix + "*" + keyword + "*"); + List list = stream.collect(Collectors.toList()); + return SaFoxUtil.searchList(list, start, size, sortType); + } + +} diff --git a/sa-token-plugin/sa-token-dao-redisson-jackson/src/main/resources/META-INF/spring.factories b/sa-token-plugin/sa-token-dao-redisson-jackson/src/main/resources/META-INF/spring.factories new file mode 100644 index 00000000..751fea99 --- /dev/null +++ b/sa-token-plugin/sa-token-dao-redisson-jackson/src/main/resources/META-INF/spring.factories @@ -0,0 +1 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=cn.dev33.satoken.dao.SaTokenDaoRedissonJackson \ No newline at end of file From 0cc497f21fca442fb8d02a03db6c8ace90b0e6d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90li?= <15040126243@163.com> Date: Tue, 3 Jan 2023 13:32:47 +0800 Subject: [PATCH 2/3] =?UTF-8?q?update=20=E6=9B=B4=E6=96=B0=E6=96=87?= =?UTF-8?q?=E6=A1=A3=E8=AF=B4=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sa-token-doc/plugin/dao-extend.md | 1 + 1 file changed, 1 insertion(+) diff --git a/sa-token-doc/plugin/dao-extend.md b/sa-token-doc/plugin/dao-extend.md index 0dc0750c..8211ecb1 100644 --- a/sa-token-doc/plugin/dao-extend.md +++ b/sa-token-doc/plugin/dao-extend.md @@ -12,6 +12,7 @@ - sa-token-dao-redisx:Redisx 集成包。 - sa-token-dao-redis-fastjson:Redis集成包,使用 fastjson 序列化方式。 - sa-token-dao-redis-fastjson2:Redis集成包,使用 fastjson2 序列化方式。 +- sa-token-dao-redisson-jackson:Redis集成包,Redisson客户端使用,jackson 序列化方式。 有关 Redis 集成,详细参考:[集成Redis](/up/integ-redis),更多存储方式欢迎提交PR From 9ce29219d6ff923c482f62f1297df7c0f72a433c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90li?= <15040126243@163.com> Date: Tue, 3 Jan 2023 13:54:16 +0800 Subject: [PATCH 3/3] =?UTF-8?q?update=20=E4=BC=98=E5=8C=96=20=E4=B8=8D?= =?UTF-8?q?=E5=BC=BA=E5=88=B6=E8=A6=86=E7=9B=96=E5=BA=8F=E5=88=97=E5=8C=96?= =?UTF-8?q?=E6=A8=A1=E5=BC=8F=20=E4=BD=BF=E7=94=A8=E6=AF=8F=E6=AC=A1?= =?UTF-8?q?=E8=B5=8B=E5=80=BC=E7=9A=84=E6=96=B9=E5=BC=8F=20=E7=81=B5?= =?UTF-8?q?=E6=B4=BB=E5=8F=98=E6=9B=B4=E5=BA=8F=E5=88=97=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dao/SaTokenDaoRedissonJackson.java | 32 +++++++++++-------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/sa-token-plugin/sa-token-dao-redisson-jackson/src/main/java/cn/dev33/satoken/dao/SaTokenDaoRedissonJackson.java b/sa-token-plugin/sa-token-dao-redisson-jackson/src/main/java/cn/dev33/satoken/dao/SaTokenDaoRedissonJackson.java index b0ea9d45..652e4571 100644 --- a/sa-token-plugin/sa-token-dao-redisson-jackson/src/main/java/cn/dev33/satoken/dao/SaTokenDaoRedissonJackson.java +++ b/sa-token-plugin/sa-token-dao-redisson-jackson/src/main/java/cn/dev33/satoken/dao/SaTokenDaoRedissonJackson.java @@ -15,6 +15,7 @@ import org.redisson.api.RBatch; import org.redisson.api.RBucket; import org.redisson.api.RBucketAsync; import org.redisson.api.RedissonClient; +import org.redisson.client.codec.Codec; import org.redisson.codec.JsonJacksonCodec; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; @@ -52,6 +53,11 @@ public class SaTokenDaoRedissonJackson implements SaTokenDao { */ public ObjectMapper objectMapper; + /** + * 序列化方式 + */ + public Codec codec; + /** * redisson 客户端 */ @@ -98,7 +104,7 @@ public class SaTokenDaoRedissonJackson implements SaTokenDao { } // 开始初始化相关组件 - redissonClient.getConfig().setCodec(new JsonJacksonCodec(objectMapper)); + this.codec = new JsonJacksonCodec(objectMapper); this.redissonClient = redissonClient; this.isInit = true; } @@ -109,7 +115,7 @@ public class SaTokenDaoRedissonJackson implements SaTokenDao { */ @Override public String get(String key) { - RBucket rBucket = redissonClient.getBucket(key); + RBucket rBucket = redissonClient.getBucket(key, codec); return rBucket.get(); } @@ -123,11 +129,11 @@ public class SaTokenDaoRedissonJackson implements SaTokenDao { } // 判断是否为永不过期 if(timeout == SaTokenDao.NEVER_EXPIRE) { - RBucket bucket = redissonClient.getBucket(key); + RBucket bucket = redissonClient.getBucket(key, codec); bucket.set(value); } else { RBatch batch = redissonClient.createBatch(); - RBucketAsync bucket = batch.getBucket(key); + RBucketAsync bucket = batch.getBucket(key, codec); bucket.setAsync(value); bucket.expireAsync(Duration.ofSeconds(timeout)); batch.execute(); @@ -152,7 +158,7 @@ public class SaTokenDaoRedissonJackson implements SaTokenDao { */ @Override public void delete(String key) { - redissonClient.getBucket(key).delete(); + redissonClient.getBucket(key, codec).delete(); } /** @@ -160,7 +166,7 @@ public class SaTokenDaoRedissonJackson implements SaTokenDao { */ @Override public long getTimeout(String key) { - RBucket rBucket = redissonClient.getBucket(key); + RBucket rBucket = redissonClient.getBucket(key, codec); long timeout = rBucket.remainTimeToLive(); return timeout < 0 ? timeout : timeout / 1000; } @@ -181,7 +187,7 @@ public class SaTokenDaoRedissonJackson implements SaTokenDao { } return; } - RBucket rBucket = redissonClient.getBucket(key); + RBucket rBucket = redissonClient.getBucket(key, codec); rBucket.expire(Duration.ofSeconds(timeout)); } @@ -192,7 +198,7 @@ public class SaTokenDaoRedissonJackson implements SaTokenDao { */ @Override public Object getObject(String key) { - RBucket rBucket = redissonClient.getBucket(key); + RBucket rBucket = redissonClient.getBucket(key, codec); return rBucket.get(); } @@ -206,11 +212,11 @@ public class SaTokenDaoRedissonJackson implements SaTokenDao { } // 判断是否为永不过期 if(timeout == SaTokenDao.NEVER_EXPIRE) { - RBucket bucket = redissonClient.getBucket(key); + RBucket bucket = redissonClient.getBucket(key, codec); bucket.set(object); } else { RBatch batch = redissonClient.createBatch(); - RBucketAsync bucket = batch.getBucket(key); + RBucketAsync bucket = batch.getBucket(key, codec); bucket.setAsync(object); bucket.expireAsync(Duration.ofSeconds(timeout)); batch.execute(); @@ -236,7 +242,7 @@ public class SaTokenDaoRedissonJackson implements SaTokenDao { */ @Override public void deleteObject(String key) { - redissonClient.getBucket(key).delete(); + redissonClient.getBucket(key, codec).delete(); } /** @@ -244,7 +250,7 @@ public class SaTokenDaoRedissonJackson implements SaTokenDao { */ @Override public long getObjectTimeout(String key) { - RBucket rBucket = redissonClient.getBucket(key); + RBucket rBucket = redissonClient.getBucket(key, codec); long timeout = rBucket.remainTimeToLive(); return timeout < 0 ? timeout : timeout / 1000; } @@ -265,7 +271,7 @@ public class SaTokenDaoRedissonJackson implements SaTokenDao { } return; } - RBucket rBucket = redissonClient.getBucket(key); + RBucket rBucket = redissonClient.getBucket(key, codec); rBucket.expire(Duration.ofSeconds(timeout)); }