【坑】Springboot+Redis序列化坑

本文记录了一次SpringBoot整合Redis过程中遇到的序列化异常问题及其解决过程。通过对比新旧系统的序列化配置,最终将SpringBoot的序列化方式调整为与老系统一致,解决了兼容性问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

今天在测试springboot整合redis的时候遇到下面这个坑,百度来百度去发现提示都是ajax的问题,真的是醉了,错误提示如下所示,不信大家可以直接复制百度一下答案是什么(流泪中。。。。),错误如下:

org.springframework.data.redis.serializer.SerializationException: Could not read JSON: Unrecognized token 'b4811c63': was expecting ('true', 'false' or 'null')

1.错误原因排查

然后就进行debug调试,发现代码一直到redisTemplate.opsForZSet().reverseRangeWithScores()这一行都没问题,然后进入redis源代码里面检查,发现是在发送redis服务的时候出现问题,所以可以断定应该是配置的问题,然后仔细检查配置,发现也没有错误,redis序列化的配置如下所示:

@Bean
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory factory) {
    RedisTemplate redisTemplate = new StringRedisTemplate(factory);
    Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
    ObjectMapper om = new ObjectMapper();
    om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
    om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
    jackson2JsonRedisSerializer.setObjectMapper(om);
    redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
    redisTemplate.afterPropertiesSet();
    return redisTemplate;
}

2.细节分析

这一条线索断掉之后,只能通过错误信息来分析了,错误信息中有一条特别奇怪,就是token 'b4811c63',然后我仔细从redis中对比,发现是之前存储的redis的key值,判断可能是redis中key存在乱码,所以就将redis的key全部清空,自己添加数据进去,发现自己添加的数据是可以的。从这一现象可以得出,应该实现老系统序列化的规则和现在springboot的序列化规则不一样导致的,查看老系统的redis配置信息,如下所示:

<!--序列化-->
<bean name="stringRedisSerializer" class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
<bean name="jdkSerializationRedisSerializer" class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer"/>
<bean id="clusterRedisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
    <property name="connectionFactory" ref="redis4CacheConnectionFactory"/>
    <property name="keySerializer" ref="stringRedisSerializer"/>
    <property name="hashKeySerializer" ref="stringRedisSerializer"/>
    <property name="valueSerializer" ref="stringRedisSerializer"/>
    <property name="hashValueSerializer" ref="stringRedisSerializer"/>
</bean>

3.问题解决

然后和我们刚才序列化的方式对比一下,发现真的是序列化方式不一样,旧的是通过StringRedisSerializer进行序列化的,springboot是通过Jackson2JsonRedisSerializer进行序列化的。所以为了兼容老系统的序列化方式,这边我将springboot也改成StringRedisSerializer的序列化方式,代码如下所示:

@Bean
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory factory) {
    RedisTemplate redisTemplate = new StringRedisTemplate(factory);
    StringRedisSerializer stringRedisSerializer =new StringRedisSerializer();
    redisTemplate.setValueSerializer(stringRedisSerializer);
    redisTemplate.setKeySerializer(stringRedisSerializer);
    redisTemplate.setHashKeySerializer(stringRedisSerializer);
    redisTemplate.setHashValueSerializer(stringRedisSerializer);
    redisTemplate.afterPropertiesSet();
    return redisTemplate;
}

改完之后,发现这个问题就没有了,redis中就可以照常插入、查询数据了。

总结:

这个问题是很典型的架构优化问题,老系统和新系统代码兼容性问题。从这个坑可以得出这样的结论:百度得到的答案很大一部分都是片面的,我们还是得根据实际情况来分析。其次就是要仔细看报错信息,不要放过一点细节,因为可能你的答案就在这一点细节中。

想要更多干货、技术猛料的孩子,快点拿起手机扫码关注我,我在这里等你哦~

                                                       

### 使用 Spring Boot、Redis 和 JWT 实现身份验证 在现代 Web 应用程序开发中,使用 JWT(JSON Web Token)作为无状态的身份验证机制是一种常见的方式。为了增强系统的安全性并支持分布式部署场景下的会话管理,可以结合 Redis 来存储和管理 JWT 的相关信息。 以下是基于 Spring Boot 3 + Spring Security 6 + JWT + Redis 的实现方案: #### 1. **项目依赖** 首先,在 `pom.xml` 文件中添加必要的依赖项: ```xml <dependencies> <!-- Spring Boot Starter --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- Spring Security --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <!-- JWT --> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-api</artifactId> <version>0.11.5</version> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-impl</artifactId> <scope>runtime</scope> <version>0.11.5</version> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-jackson</artifactId> <scope>runtime</scope> <version>0.11.5</version> </dependency> <!-- Redis --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <!-- MySQL and MyBatis (Optional, if using database for user management) --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.3.1</version> </dependency> </dependencies> ``` --- #### 2. **JWT 工具类** 创建一个工具类用于生成和解析 JWT Token。 ```java import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; public class JwtUtil { private static final String SECRET_KEY = "your_secret_key"; // 密钥需安全保存 public String generateToken(String username) { return Jwts.builder() .setSubject(username) .signWith(SignatureAlgorithm.HS256, SECRET_KEY) .compact(); } public Claims parseToken(String token) { return Jwts.parser() .setSigningKey(SECRET_KEY) .parseClaimsJws(token) .getBody(); } } ``` --- #### 3. **Redis 配置** 通过 Redis 缓存用户的 Token 及其有效期。 ```java @Configuration public class RedisConfig { @Bean public LettuceConnectionFactory redisConnectionFactory() { return new LettuceConnectionFactory(); // 默认连接本地 Redis } @Bean public RedisTemplate<String, Object> redisTemplate() { RedisTemplate<String, Object> template = new RedisTemplate<>(); template.setConnectionFactory(redisConnectionFactory()); template.setValueSerializer(new GenericJackson2JsonRedisSerializer()); // JSON序列化 return template; } } ``` --- #### 4. **登录逻辑** 当用户成功登录后,生成 JWT 并将其存储到 Redis 中。 ```java @RestController @RequestMapping("/auth") public class AuthController { @Autowired private UserService userService; // 假设有一个服务来处理用户数据 @Autowired private JwtUtil jwtUtil; @Autowired private RedisTemplate<String, Object> redisTemplate; @PostMapping("/login") public ResponseEntity<?> login(@RequestBody LoginRequest request) { User user = userService.authenticate(request.getUsername(), request.getPassword()); if (user != null) { String token = jwtUtil.generateToken(user.getUsername()); // 将 Token 存入 Redis,设置过期时间为 1 天 redisTemplate.opsForValue().set("AUTH:" + token, user.getId(), Duration.ofDays(1)); return ResponseEntity.ok(Map.of("token", token)); } else { throw new RuntimeException("Invalid credentials"); } } } ``` --- #### 5. **拦截器与过滤器** 在请求到达控制器之前,检查 Token 是否有效以及是否存在 Redis 中。 ```java @Component public class JwtAuthenticationFilter extends OncePerRequestFilter { @Autowired private JwtUtil jwtUtil; @Autowired private RedisTemplate<String, Object> redisTemplate; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { String authHeader = request.getHeader("Authorization"); if (authHeader != null && authHeader.startsWith("Bearer ")) { try { String token = authHeader.substring(7); // 提取 Token // 检查 Token 是否存在于 Redis 中 Boolean existsInRedis = redisTemplate.hasKey("AUTH:" + token); if (!existsInRedis) { response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); return; } // 解析 Token 获取用户名 Claims claims = jwtUtil.parseToken(token); UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken( claims.getSubject(), null, Collections.emptyList() ); SecurityContextHolder.getContext().setAuthentication(authentication); } catch (Exception e) { response.setStatus(HttpServletResponse.SC_FORBIDDEN); return; } } filterChain.doFilter(request, response); } } ``` --- #### 6. **Spring Security 配置** 配置 Spring Security 以启用自定义过滤器。 ```java @EnableWebSecurity public class SecurityConfig { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http.csrf().disable() .authorizeHttpRequests() .requestMatchers("/auth/login").permitAll() .anyRequest().authenticated() .and() .addFilterBefore(new JwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class); return http.build(); } } ``` --- ### 总结 上述方法展示了如何利用 Spring Boot、Redis 和 JWT 构建一套完整的身份验证解决方案[^1]。该方案不仅实现了无状态的 Token 认证,还借助 Redis 提供了高效的 Token 管理能力[^4]。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值