mirror of
https://gitee.com/dromara/sa-token.git
synced 2025-10-21 02:57:23 +08:00
feat: 新增 SaTokenPlugin 接口,重构插件体系
This commit is contained in:
@@ -18,6 +18,8 @@
|
||||
|
||||
<!-- 所有子模块 -->
|
||||
<modules>
|
||||
<module>sa-token-jackson</module>
|
||||
|
||||
<module>sa-token-redis</module>
|
||||
<module>sa-token-redis-jackson</module>
|
||||
<module>sa-token-redis-fastjson</module>
|
||||
|
41
sa-token-plugin/sa-token-jackson/pom.xml
Normal file
41
sa-token-plugin/sa-token-jackson/pom.xml
Normal file
@@ -0,0 +1,41 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-plugin</artifactId>
|
||||
<version>${revision}</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>sa-token-jackson</name>
|
||||
<artifactId>sa-token-jackson</artifactId>
|
||||
<description>sa-token-jackson</description>
|
||||
|
||||
<dependencies>
|
||||
<!-- sa-token-core -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-core</artifactId>
|
||||
</dependency>
|
||||
<!-- jackson-databind -->
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<!-- jackson-datatype-jsr310 -->
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.datatype</groupId>
|
||||
<artifactId>jackson-datatype-jsr310</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
|
||||
</project>
|
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* Copyright 2020-2099 sa-token.cc
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package cn.dev33.satoken.dao;
|
||||
|
||||
import cn.dev33.satoken.exception.SaJsonConvertException;
|
||||
import cn.dev33.satoken.json.SaJsonTemplate;
|
||||
import cn.dev33.satoken.util.SaFoxUtil;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||
import com.fasterxml.jackson.databind.jsontype.BasicPolymorphicTypeValidator;
|
||||
import com.fasterxml.jackson.databind.jsontype.PolymorphicTypeValidator;
|
||||
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 java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* JSON 转换器, Jackson 版实现
|
||||
*
|
||||
* @author click33
|
||||
* @since 1.34.0
|
||||
*/
|
||||
public class SaJsonTemplateForJackson implements SaJsonTemplate {
|
||||
|
||||
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);
|
||||
|
||||
/**
|
||||
* 底层 Mapper 对象
|
||||
*/
|
||||
public ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
public SaJsonTemplateForJackson() {
|
||||
|
||||
// 1、使 objectMapper 序列化时带上类型信息,以便该 json 字符串可以成功反序列化
|
||||
// 构建反序列化限制器,此处可以限制只允许指定类型或指定包下的类型才可以反序列化,此处指定所有类型都可以反序列化
|
||||
PolymorphicTypeValidator ptv = BasicPolymorphicTypeValidator.builder()
|
||||
// 允许所有子类型反序列化(即反序列化时遇到的类)
|
||||
.allowIfSubType(Object.class)
|
||||
// 允许所有基类型反序列化(如 Object、自定义抽象类)
|
||||
.allowIfBaseType(Object.class)
|
||||
.build();
|
||||
// 启用全局默认类型(嵌入类型信息)
|
||||
objectMapper.activateDefaultTyping(
|
||||
ptv,
|
||||
// 对非 final 类嵌入类型信息
|
||||
ObjectMapper.DefaultTyping.NON_FINAL,
|
||||
// 类型信息以属性形式存在("@class")
|
||||
JsonTypeInfo.As.PROPERTY
|
||||
);
|
||||
|
||||
// 2、使空 bean 在序列化时也能记录类型信息,而不是只序列化成 {}
|
||||
objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
|
||||
|
||||
// 3、配置 [ 忽略未知字段 ]
|
||||
this.objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||
|
||||
// 4、配置 [ 时间类型转换 ]
|
||||
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);
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 序列化:对象 -> json 字符串
|
||||
*/
|
||||
@Override
|
||||
public String objectToJson(Object obj) {
|
||||
try {
|
||||
return objectMapper.writeValueAsString(obj);
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new SaJsonConvertException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 反序列化:json 字符串 → 对象
|
||||
*/
|
||||
@Override
|
||||
public Object jsonToObject(String jsonStr) {
|
||||
if(SaFoxUtil.isEmpty(jsonStr)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
Object value = objectMapper.readValue(jsonStr, Object.class);
|
||||
return value;
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new SaJsonConvertException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 反序列化:json 字符串 → Map
|
||||
*/
|
||||
@Override
|
||||
public Map<String, Object> jsonToMap(String jsonStr) {
|
||||
if(SaFoxUtil.isEmpty(jsonStr)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> map = objectMapper.readValue(jsonStr, Map.class);
|
||||
return map;
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new SaJsonConvertException(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2020-2099 sa-token.cc
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package cn.dev33.satoken.plugin.impl;
|
||||
|
||||
import cn.dev33.satoken.SaManager;
|
||||
import cn.dev33.satoken.dao.SaJsonTemplateForJackson;
|
||||
import cn.dev33.satoken.plugin.SaTokenPlugin;
|
||||
|
||||
/**
|
||||
* SaToken 插件:JSON 转换器安装
|
||||
*
|
||||
* @author click33
|
||||
* @since 1.41.0
|
||||
*/
|
||||
public class SaTokenPluginForJackson implements SaTokenPlugin {
|
||||
|
||||
@Override
|
||||
public void setup() {
|
||||
SaManager.setSaJsonTemplate(new SaJsonTemplateForJackson());
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1 @@
|
||||
cn.dev33.satoken.plugin.impl.SaTokenPluginForJackson
|
@@ -17,28 +17,11 @@ 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.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
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.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
@@ -53,35 +36,11 @@ import java.util.concurrent.TimeUnit;
|
||||
@Component
|
||||
public class SaTokenDaoRedisJackson 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 作用域暴露出此对象,方便开发者二次更改配置)
|
||||
*
|
||||
* <p> 例如:
|
||||
* <pre>
|
||||
* SaTokenDaoRedisJackson redisJackson = (SaTokenDaoRedisJackson) SaManager.getSaTokenDao();
|
||||
* redisJackson.objectMapper.xxx = xxx;
|
||||
* </pre>
|
||||
* </p>
|
||||
*/
|
||||
public ObjectMapper objectMapper;
|
||||
|
||||
/**
|
||||
* String 读写专用
|
||||
*/
|
||||
public StringRedisTemplate stringRedisTemplate;
|
||||
|
||||
/**
|
||||
* Object 读写专用
|
||||
*/
|
||||
public RedisTemplate<String, Object> objectRedisTemplate;
|
||||
|
||||
/**
|
||||
* 标记:是否已初始化成功
|
||||
*/
|
||||
@@ -93,59 +52,15 @@ public class SaTokenDaoRedisJackson implements SaTokenDao {
|
||||
if(this.isInit) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 指定相应的序列化方案
|
||||
StringRedisSerializer keySerializer = new StringRedisSerializer();
|
||||
GenericJackson2JsonRedisSerializer valueSerializer = new GenericJackson2JsonRedisSerializer();
|
||||
|
||||
// 通过反射获取Mapper对象, 增加一些配置, 增强兼容性
|
||||
try {
|
||||
Field field = GenericJackson2JsonRedisSerializer.class.getDeclaredField("mapper");
|
||||
field.setAccessible(true);
|
||||
this.objectMapper = (ObjectMapper) field.get(valueSerializer);
|
||||
|
||||
// 配置[忽略未知字段]
|
||||
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.instance.createSession = (sessionId) -> new SaSessionForJacksonCustomized(sessionId);
|
||||
} catch (Exception e) {
|
||||
System.err.println(e.getMessage());
|
||||
}
|
||||
// 构建StringRedisTemplate
|
||||
StringRedisTemplate stringTemplate = new StringRedisTemplate();
|
||||
stringTemplate.setConnectionFactory(connectionFactory);
|
||||
stringTemplate.afterPropertiesSet();
|
||||
|
||||
// 构建RedisTemplate
|
||||
RedisTemplate<String, Object> template = new RedisTemplate<>();
|
||||
template.setConnectionFactory(connectionFactory);
|
||||
template.setKeySerializer(keySerializer);
|
||||
template.setHashKeySerializer(keySerializer);
|
||||
template.setValueSerializer(valueSerializer);
|
||||
template.setHashValueSerializer(valueSerializer);
|
||||
template.afterPropertiesSet();
|
||||
|
||||
// 开始初始化相关组件
|
||||
this.stringRedisTemplate = stringTemplate;
|
||||
this.objectRedisTemplate = template;
|
||||
|
||||
// 重写 SaSession 生成策略
|
||||
SaStrategy.instance.createSession = (sessionId) -> new SaSessionForJacksonCustomized(sessionId);
|
||||
|
||||
// 打上标记,表示已经初始化成功,后续无需再重新初始化
|
||||
this.isInit = true;
|
||||
|
Reference in New Issue
Block a user