跳转到内容

Session 共享


单机部署时,Session 存储在服务器内存中,没有问题。多实例部署后,负载均衡将请求分发到不同实例,Session 无法共享:

单机(正常):
客户端 ──▶ 实例 A ──▶ 内存 Session ✅
多实例(Session 丢失):
登录请求 ──▶ 实例 A(Session 存在 A 的内存)
后续请求 ──▶ 实例 B(B 的内存没有该 Session)──▶ 未登录 ❌
后续请求 ──▶ 实例 C(C 的内存没有该 Session)──▶ 未登录 ❌

三种解决方案:

方案原理优点缺点
IP 粘滞(Sticky Session)同一 IP 固定路由到同一实例无需改造实例宕机 Session 丢失,负载不均
Session 复制每个实例同步全量 Session无单点问题网络开销大,内存占用高
集中式 Session 存储Session 存 Redis,所有实例共享扩展性好,推荐 ✅依赖 Redis 可用性

Spring Session 是 Spring 提供的 Session 管理框架,无缝替换原生 HttpSession,将 Session 数据透明地存储到 Redis,业务代码无需任何改动。

请求 ──▶ Spring Session 过滤器
├── 从 Cookie 取 sessionId
├── 用 sessionId 从 Redis 读取 Session 数据
└── 包装为 HttpSession 传入业务代码
业务代码:request.getSession() ──▶ 实际读写 Redis(透明)
<!-- Spring Session Redis -->
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
<!-- Redis 客户端 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
spring:
data:
redis:
host: localhost
port: 6379
password: 123456
session:
store-type: redis # 使用 Redis 存储 Session
timeout: 30m # Session 过期时间,默认 30 分钟
redis:
namespace: spring:session # Redis 中 key 的命名空间前缀
flush-mode: on-save # 何时将 Session 写入 Redis:on-save(修改时)/ immediate(立即)
@SpringBootApplication
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 1800) // 1800s = 30 分钟
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

Spring Session 集成后,业务代码照常使用 HttpSession,无需任何修改:

@RestController
@RequestMapping("/auth")
public class AuthController {
// 登录:将用户信息存入 Session
@PostMapping("/login")
public String login(@RequestParam String username,
@RequestParam String password,
HttpSession session) {
User user = authService.verify(username, password);
if (user == null) return "用户名或密码错误";
session.setAttribute("currentUser", user); // 实际写入 Redis
return "登录成功,sessionId: " + session.getId();
}
// 获取当前用户:从 Session 读取
@GetMapping("/me")
public User getCurrentUser(HttpSession session) {
return (User) session.getAttribute("currentUser"); // 实际从 Redis 读取
}
// 登出:清除 Session
@PostMapping("/logout")
public String logout(HttpSession session) {
session.invalidate(); // 删除 Redis 中的 Session 数据
return "已退出登录";
}
}

Spring Session 在 Redis 中以 Hash 结构存储 Session 数据:

spring:session:sessions:<sessionId> ← Hash,存储 Session 属性
├── sessionAttr:currentUser → {"id":1,"name":"Alice",...}
├── creationTime → 1710000000000
├── lastAccessedTime → 1710000060000
└── maxInactiveInterval → 1800
spring:session:sessions:expires:<sessionId> ← String,用于过期通知
spring:session:expirations:<时间戳> ← Set,记录该时刻过期的 sessionId
Terminal window
# 在 Redis 管理台中查看 Session
KEYS spring:session:sessions:*
HGETALL spring:session:sessions:<sessionId>
TTL spring:session:sessions:<sessionId>

Spring Session 默认通过 Cookie 传递 sessionId,可按需自定义:

@Configuration
public class SessionConfig {
@Bean
public CookieSerializer cookieSerializer() {
DefaultCookieSerializer serializer = new DefaultCookieSerializer();
serializer.setCookieName("SESSION"); // 自定义 Cookie 名(默认 SESSION)
serializer.setDomainName("example.com"); // 跨子域共享 Session
serializer.setCookiePath("/");
serializer.setHttpOnly(true); // 禁止 JS 访问
serializer.setUseSecureCookie(true); // 仅 HTTPS 传输
serializer.setCookieMaxAge(1800); // Cookie 有效期(秒)
return serializer;
}
}

Q:Session 中存储的对象需要实现 Serializable 吗?

默认使用 JDK 序列化时需要。若配置了 JSON 序列化则不需要:

@Bean
public RedisSerializer<Object> springSessionDefaultRedisSerializer() {
// 使用 JSON 序列化替代 JDK 序列化
return new GenericJackson2JsonRedisSerializer();
}

Q:多个服务(微服务)如何共享 Session?

所有服务指向同一个 Redis,并配置相同的 namespacecookieName,即可实现跨服务 Session 共享(单点登录基础方案)。

Q:Session 过期后如何通知业务?

// 实现 HttpSessionListener 或监听 Spring Session 事件
@Component
public class SessionExpiredListener
implements ApplicationListener<SessionExpiredEvent> {
@Override
public void onApplicationEvent(SessionExpiredEvent event) {
String sessionId = event.getSessionId();
log.info("Session 过期:{}", sessionId);
// 可在此处清理业务数据,如在线用户列表
}
}

传统 Session(单机):
实例 A ──▶ 内存 Session
↑ 仅本实例可见
Spring Session + Redis(分布式):
实例 A ──▶ Redis(集中存储)◀── 实例 B
实例 C ──▶ ↑
所有实例共享,水平扩展无限制