---
title: "05-redis与session"
created: 2025-12-02
tags:
- 项目
aliases:
- redis与session
---
# redis与session
## 基本引入
本质就是map 以key value的形式进行一个缓存 所以简单使用的时候也当作map用就行了
加入依赖
```xml
org.springframework.boot
spring-boot-starter-data-redis
```
修改配置文件
```yaml
spring:
redis:
host: localhost # Redis 主机
port: 6379 # Redis 端口
database: 2 # Redis 使用的数据库索引
```
使用只需要简单设置key
然后注入
@Resource
private RedisTemplate redisTemplate;
使用其get set方法即可
以下是一个简单的示例 实现密码重试过多锁定功能
```java
private static final String PASSWORD_RETRY_KEY = "YunPicture:user:password_retry:%s";
@Value("${user.max-password-retry}")
private int maxPwdRetryLimit;
@Override
public LoginUserVO userLogin(String userAccount, String userPassword, HttpServletRequest request) {
User user = getUserByAccount(userAccount);
//密码重试过多锁定 (redis) done
String retryKey = String.format(PASSWORD_RETRY_KEY, userAccount);
Integer retryCount = (Integer) redisTemplate.opsForValue().get(retryKey);
if (retryCount != null && retryCount >= maxPwdRetryLimit) {
throw new BusinessException(ErrorCode.FORBIDDEN_ERROR, "密码输入次数过多,请10分钟后重试");
}
boolean isPasswordValid = passwordUtils.verifyPassword(userPassword, user.getUserPassword(), user.getSalt());
if (!isPasswordValid) {
// 密码错误时记录次数
redisTemplate.opsForValue().set(retryKey, retryCount != null ? retryCount + 1 : 1, 600, TimeUnit.SECONDS);
throw new BusinessException(ErrorCode.PARAMS_ERROR, "密码错误");
} else {
// 登录成功重置计数器
redisTemplate.delete(retryKey);
}
LoginUserVO LoginuserVO = convertToLoginUserVO(user);
//todo 引入redis 控制登入设备数量
request.getSession().setAttribute(USER_LOGIN_STATE, LoginuserVO);
return LoginuserVO;
}
```
测试下来功能是没问题的 但是打开quickredis一看全是乱码
这是因为默认情况下,Spring Boot的RedisTemplate使用JdkSerializationRedisSerializer作为键/值的序列化器,这会导致:
键名会被序列化为二进制格式
QuickRedis等客户端无法直接识别二进制键名
即使数据存储成功,但通过客户端查看时显示为乱码或无法识别
(虽然没影响 但分别调试 最好还是修改以下)
## 配置序列化
其实就是简单去规定一下键值对的存储格式
自定义一下redistemplate
```java
package com.zwnsyw.yunpicturebackend.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.BasicPolymorphicTypeValidator;
import com.zwnsyw.yunpicturebackend.exception.BusinessException;
import com.zwnsyw.yunpicturebackend.exception.ErrorCode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.RedisConnectionFailureException;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
@Configuration
@Slf4j
public class RedisTemplateConfig {
@Value("${redis.host:localhost}")
private String redisHost;
@Value("${redis.port:6379}")
private int redisPort;
/**
* 配置 RedisTemplate 使用 Jackson 序列化
*/
@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);
// 键的序列化:纯文本
template.setKeySerializer(RedisSerializer.string());
template.setHashKeySerializer(RedisSerializer.string());
// 值的序列化:JSON 格式
GenericJackson2JsonRedisSerializer valueSerializer = genericJackson2JsonRedisSerializer();
template.setValueSerializer(valueSerializer);
template.setHashValueSerializer(valueSerializer);
template.setDefaultSerializer(valueSerializer);
template.setEnableTransactionSupport(true); // 开启事务支持
template.afterPropertiesSet();
try {
template.afterPropertiesSet();
} catch (RedisConnectionFailureException e) {
log.error("Redis 连接失败:主机:{},端口:{}", redisHost, redisPort);
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "连接 Redis 失败");
} catch (Exception e) {
log.error("Redis 配置失败:主机:{},端口:{}", redisHost, redisPort);
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "Redis 配置出错");
}
log.info("Redis 配置成功,主机:{},端口:{}", redisHost, redisPort);
return template;
}
/**
* 配置通用 JSON 序列化器
*/
private GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
objectMapper.activateDefaultTyping(
BasicPolymorphicTypeValidator.builder()
// 允许的业务包路径
.allowIfSubType("com.zwnsyw")
.allowIfSubType("com.zwnsyw.yunpicturebackend.model.*")
.allowIfSubType("com.zwnsyw.yunpicturebackend.model.entity.*")
.allowIfSubType("com.zwnsyw.yunpicturebackend.model.vo.*")
// 允许的集合类型
.allowIfSubType("java.util.ArrayList")
.allowIfSubType("java.util.HashMap")
.allowIfSubType("java.util.LinkedList")
.allowIfSubType("java.util.TreeMap")
.allowIfSubType("java.util.LinkedHashMap")
// 允许基础类型和 Date
.allowIfSubType("java.lang.String")
.allowIfSubType("java.lang.Long")
.allowIfSubType("java.lang.Integer")
.allowIfSubType("java.util.Date")
// 其他类型(如 MyBatis 分页类)
.allowIfSubType("com.baomidou.mybatisplus.extension.plugins.pagination.Page")
.allowIfSubType("java.sql.Timestamp")
.build(),
ObjectMapper.DefaultTyping.NON_FINAL
);
return new GenericJackson2JsonRedisSerializer(objectMapper);
}
}
```
## 配置分布式session
需引入
```xml
org.springframework.session
spring-session-data-redis
2.6.3
```
配置
```yaml
session:
store-type: redis
redis:
namespace: session # 设置 Redis 中存储 session 数据的命名空间
flush-mode: on_save # 表示当 session 更新时,才会将数据保存到 Redis
timeout: 86400 # session 过期时间:24小时
```
SessionConfig
```java
package com.zwnsyw.yunpicturebackend.config;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
import java.time.Duration;
@Configuration
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 86400*30)
public class SessionConfig {
// 配置Lettuce客户端参数
@Bean
public LettuceClientConfiguration redisSessionConfig() {
return LettuceClientConfiguration.builder()
.commandTimeout(Duration.ofMinutes(30))
.shutdownTimeout(Duration.ofSeconds(10)) // 添加关闭超时配置
.build();
}
@Bean
@Qualifier("springSessionDefaultRedisSerializer")
public RedisSerializer