fix(security): 修复RedisTokenManager会话反序列化类型转换异常
- 提取JsonMapper为共享Bean,统一序列化配置 - 使用convertValue替代直接强转,正确处理嵌套泛型
This commit is contained in:
@@ -24,10 +24,12 @@ public class RedisConfig {
|
|||||||
* 修改 Redis 序列化方式,默认 JdkSerializationRedisSerializer
|
* 修改 Redis 序列化方式,默认 JdkSerializationRedisSerializer
|
||||||
*
|
*
|
||||||
* @param redisConnectionFactory {@link RedisConnectionFactory}
|
* @param redisConnectionFactory {@link RedisConnectionFactory}
|
||||||
|
* @param jsonMapper Jackson 序列化器(统一使用不写入类型信息的配置)
|
||||||
* @return {@link RedisTemplate}
|
* @return {@link RedisTemplate}
|
||||||
*/
|
*/
|
||||||
@Bean
|
@Bean
|
||||||
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
|
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory,
|
||||||
|
JsonMapper jsonMapper) {
|
||||||
|
|
||||||
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
|
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
|
||||||
redisTemplate.setConnectionFactory(redisConnectionFactory);
|
redisTemplate.setConnectionFactory(redisConnectionFactory);
|
||||||
@@ -37,10 +39,6 @@ public class RedisConfig {
|
|||||||
redisTemplate.setHashKeySerializer(RedisSerializer.string());
|
redisTemplate.setHashKeySerializer(RedisSerializer.string());
|
||||||
|
|
||||||
// Value 使用自定义 JSON 序列化(不写入类型信息,避免 HashSet 等集合被序列化成带 @class 的结构)
|
// Value 使用自定义 JSON 序列化(不写入类型信息,避免 HashSet 等集合被序列化成带 @class 的结构)
|
||||||
JsonMapper jsonMapper = JsonMapper.builder()
|
|
||||||
.disable(DateTimeFeature.WRITE_DATES_AS_TIMESTAMPS)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
JacksonJsonRedisSerializer<Object> jsonSerializer = new JacksonJsonRedisSerializer<>(jsonMapper, Object.class);
|
JacksonJsonRedisSerializer<Object> jsonSerializer = new JacksonJsonRedisSerializer<>(jsonMapper, Object.class);
|
||||||
|
|
||||||
redisTemplate.setValueSerializer(jsonSerializer);
|
redisTemplate.setValueSerializer(jsonSerializer);
|
||||||
@@ -50,4 +48,17 @@ public class RedisConfig {
|
|||||||
return redisTemplate;
|
return redisTemplate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 统一的 JsonMapper Bean
|
||||||
|
* <p>
|
||||||
|
* 禁止将日期序列化为时间戳;不写入类型信息,保证 Redis 存储的 JSON 纯净可读。
|
||||||
|
* 需要反序列化到特定类型时,调用方应使用 {@link JsonMapper#convertValue(Object, Class)} 显式转换。
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public JsonMapper jsonMapper() {
|
||||||
|
return JsonMapper.builder()
|
||||||
|
.disable(DateTimeFeature.WRITE_DATES_AS_TIMESTAMPS)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ import org.springframework.security.core.GrantedAuthority;
|
|||||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import tools.jackson.databind.json.JsonMapper;
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
@@ -46,10 +48,14 @@ public class RedisTokenManager implements TokenManager {
|
|||||||
|
|
||||||
private final SecurityProperties securityProperties;
|
private final SecurityProperties securityProperties;
|
||||||
private final RedisTemplate<String, Object> redisTemplate;
|
private final RedisTemplate<String, Object> redisTemplate;
|
||||||
|
private final JsonMapper jsonMapper;
|
||||||
|
|
||||||
public RedisTokenManager(SecurityProperties securityProperties, RedisTemplate<String, Object> redisTemplate) {
|
public RedisTokenManager(SecurityProperties securityProperties,
|
||||||
|
RedisTemplate<String, Object> redisTemplate,
|
||||||
|
JsonMapper jsonMapper) {
|
||||||
this.securityProperties = securityProperties;
|
this.securityProperties = securityProperties;
|
||||||
this.redisTemplate = redisTemplate;
|
this.redisTemplate = redisTemplate;
|
||||||
|
this.jsonMapper = jsonMapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -96,8 +102,9 @@ public class RedisTokenManager implements TokenManager {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Authentication parseToken(String token) {
|
public Authentication parseToken(String token) {
|
||||||
UserSession userSession = (UserSession) redisTemplate.opsForValue().get(formatTokenKey(token));
|
Object raw = redisTemplate.opsForValue().get(formatTokenKey(token));
|
||||||
if (userSession == null) return null;
|
if (raw == null) return null;
|
||||||
|
UserSession userSession = jsonMapper.convertValue(raw, UserSession.class);
|
||||||
|
|
||||||
// 构建用户权限集合
|
// 构建用户权限集合
|
||||||
Set<SimpleGrantedAuthority> authorities = null;
|
Set<SimpleGrantedAuthority> authorities = null;
|
||||||
@@ -144,11 +151,12 @@ public class RedisTokenManager implements TokenManager {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public AuthenticationToken refreshToken(String refreshToken) {
|
public AuthenticationToken refreshToken(String refreshToken) {
|
||||||
UserSession userSession = (UserSession) redisTemplate.opsForValue()
|
Object raw = redisTemplate.opsForValue()
|
||||||
.get(StrUtil.format(RedisConstants.Auth.REFRESH_TOKEN_USER, refreshToken));
|
.get(StrUtil.format(RedisConstants.Auth.REFRESH_TOKEN_USER, refreshToken));
|
||||||
if (userSession == null) {
|
if (raw == null) {
|
||||||
throw new TokenInvalidException(ResultCode.REFRESH_TOKEN_INVALID);
|
throw new TokenInvalidException(ResultCode.REFRESH_TOKEN_INVALID);
|
||||||
}
|
}
|
||||||
|
UserSession userSession = jsonMapper.convertValue(raw, UserSession.class);
|
||||||
Object oldAccessTokenValue = redisTemplate.opsForValue().get(StrUtil.format(RedisConstants.Auth.USER_ACCESS_TOKEN, userSession.getUserId()));
|
Object oldAccessTokenValue = redisTemplate.opsForValue().get(StrUtil.format(RedisConstants.Auth.USER_ACCESS_TOKEN, userSession.getUserId()));
|
||||||
// 删除旧的访问令牌记录
|
// 删除旧的访问令牌记录
|
||||||
Optional.of(oldAccessTokenValue)
|
Optional.of(oldAccessTokenValue)
|
||||||
|
|||||||
Reference in New Issue
Block a user