This commit is contained in:
zlwu committed 2026-09-28 10:09:09 +08:00
1 parent 623f5b65e7
commit 103a53f6f3
3 files changed
+58 -6

No files matched your search

@@ -2,9 +2,11 @@ package org.dromara.common.redis.config;
import cn.hutool.core.util.ObjectUtil;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
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.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
@@ -51,8 +53,19 @@ public class RedisConfig {
om.registerModule(javaTimeModule);
om.setTimeZone(TimeZone.getDefault());
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
// 指定序列化输入的类型,类必须是非final修饰的。序列化时将对象全类名一起保存下来
om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);
// 使用包名白名单替代 LaissezFaireSubTypeValidator,降低 Redis 被写入恶意 @class 时的反序列化 RCE 风险
// 说明:RedisUtils/Sa-Token 以 Object 读写缓存,仍需保留类型信息;禁止完全放开任意类反序列化
PolymorphicTypeValidator ptv = BasicPolymorphicTypeValidator.builder()
.allowIfSubType("org.dromara.")
.allowIfSubType("cn.dev33.satoken.")
.allowIfSubType("cn.hutool.")
.allowIfSubType("java.util.")
.allowIfSubType("java.lang.")
.allowIfSubType("java.time.")
.allowIfSubType("java.math.")
.allowIfSubType("java.util.concurrent.")
.build();
om.activateDefaultTyping(ptv, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
// LoggerFactory.useSlf4jLogging(true);
// FuryCodec furyCodec = new FuryCodec();
// CompositeCodec codec = new CompositeCodec(StringCodec.INSTANCE, furyCodec, furyCodec);
@@ -1,5 +1,10 @@
package org.dromara.xg.config;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.BasicPolymorphicTypeValidator;
import com.fasterxml.jackson.databind.jsontype.PolymorphicTypeValidator;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
@@ -14,6 +19,7 @@ import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;
import java.util.TimeZone;
/**
* 学工模块缓存配置类
@@ -31,11 +37,25 @@ public class XgCacheConfig {
*/
@Bean("xgCacheManager")
public CacheManager xgCacheManager(RedisConnectionFactory factory) {
ObjectMapper om = new ObjectMapper();
om.registerModule(new JavaTimeModule());
om.setTimeZone(TimeZone.getDefault());
// 白名单限制可反序列化类型,避免 GenericJackson2JsonRedisSerializer 默认 LaissezFaire 风险
PolymorphicTypeValidator ptv = BasicPolymorphicTypeValidator.builder()
.allowIfSubType("org.dromara.")
.allowIfSubType("java.util.")
.allowIfSubType("java.lang.")
.allowIfSubType("java.time.")
.allowIfSubType("java.math.")
.build();
om.activateDefaultTyping(ptv, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
GenericJackson2JsonRedisSerializer valueSerializer = new GenericJackson2JsonRedisSerializer(om);
// 默认缓存配置
RedisCacheConfiguration defaultConfig = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofMinutes(30)) // 默认30分钟过期
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(valueSerializer))
.disableCachingNullValues(); // 不缓存null值
// 特定缓存配置
@@ -6,6 +6,7 @@ import lombok.RequiredArgsConstructor;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.*;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.annotation.SaMode;
import org.springframework.web.bind.annotation.*;
import org.springframework.validation.annotation.Validated;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
@@ -70,8 +71,15 @@ public class XgStudentStatusChangeController extends BaseController {
/**
* 新增学生学籍异动
* <p>权限与前端按钮对齐:退学/休学/复学/毕业 任一按钮权限,或历史 add 权限均可</p>
*/
@SaCheckPermission("studentMsg:studentStatusChange:add")
@SaCheckPermission(value = {
"studentMsg:status:tuixue",
"studentMsg:status:xiuxue",
"studentMsg:status:fuxue",
"studentMsg:status:biye",
"studentMsg:studentStatusChange:add"
}, mode = SaMode.OR)
@Log(title = "学生学籍异动", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping()
@@ -81,8 +89,15 @@ public class XgStudentStatusChangeController extends BaseController {
/**
* 批量新增学生学籍异动(用于前端提交多个学生的异动)
* <p>权限与前端按钮对齐:退学/休学/复学/毕业 任一按钮权限,或历史 add 权限均可</p>
*/
@SaCheckPermission("studentMsg:studentStatusChange:add")
@SaCheckPermission(value = {
"studentMsg:status:tuixue",
"studentMsg:status:xiuxue",
"studentMsg:status:fuxue",
"studentMsg:status:biye",
"studentMsg:studentStatusChange:add"
}, mode = SaMode.OR)
@Log(title = "学生学籍异动", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping("/batch")
@@ -97,6 +112,10 @@ public class XgStudentStatusChangeController extends BaseController {
/**
* 复学并换班级(一次提交,事务化写入两条记录)
*/
@SaCheckPermission(value = {
"studentMsg:status:fuxue",
"studentMsg:studentStatusChange:add"
}, mode = SaMode.OR)
@Log(title = "学生复学并换班级", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping("/resume-with-class-change")