HTTP 协议本身是无状态的,这意味着服务器不会记住来自客户端的先前请求。但在实际应用中,我们需要服务器能够识别用户身份,并保持用户的会话状态,例如用户的登录状态、购物车信息等。本文将深入探讨在 Spring Boot 中如何利用 Cookie 和 Session 这两种技术来实现 无状态 HTTP 的“记忆”,并结合实际案例进行全栈实战。
Cookie:客户端的“小纸条”
Cookie 的原理
Cookie 是一种存储在用户浏览器上的小型文本文件,由服务器发送给客户端,客户端在后续的请求中会自动携带 Cookie。服务器通过读取 Cookie 中的信息来识别用户身份或者跟踪用户的行为。
Spring Boot 中 Cookie 的使用
在 Spring Boot 中,我们可以通过 HttpServletResponse 和 HttpServletRequest 对象来操作 Cookie。以下是一个简单的例子:
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class CookieController {
@GetMapping("/setCookie")
public String setCookie(HttpServletResponse response) {
// 创建 Cookie
Cookie cookie = new Cookie("username", "axiaoyu"); // Cookie 名称和值
cookie.setMaxAge(7 * 24 * 60 * 60); // 设置 Cookie 的有效期,单位秒,这里设置为 7 天
cookie.setPath("/"); // 设置 Cookie 的作用路径
// 将 Cookie 添加到响应中
response.addCookie(cookie);
return "Cookie 已设置";
}
@GetMapping("/getCookie")
public String getCookie(HttpServletRequest request) {
Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (Cookie cookie : cookies) {
if (cookie.getName().equals("username")) {
return "Cookie 值:" + cookie.getValue();
}
}
}
return "未找到 username Cookie";
}
}
Cookie 的安全问题
Cookie 存在一些安全问题,例如:
- Cookie 欺骗:用户可以修改本地的 Cookie 值,从而冒充其他用户。
- Cookie 窃取:通过 XSS 攻击等方式,攻击者可以获取用户的 Cookie 信息。
为了提高 Cookie 的安全性,可以采取以下措施:
- 设置 HttpOnly 属性:禁止 JavaScript 访问 Cookie,防止 XSS 攻击。
cookie.setHttpOnly(true); - 设置 Secure 属性:只允许在 HTTPS 连接中传输 Cookie。
cookie.setSecure(true); - 使用签名 Cookie:对 Cookie 的值进行签名,防止用户篡改 Cookie。
Session:服务器的“备忘录”
Session 的原理
Session 是一种服务器端的技术,用于存储用户的会话信息。当用户第一次访问服务器时,服务器会创建一个 Session 对象,并生成一个 Session ID,然后将 Session ID 通过 Cookie 发送给客户端。客户端在后续的请求中会携带 Session ID,服务器根据 Session ID 找到对应的 Session 对象,从而识别用户身份。
Spring Boot 中 Session 的使用
Spring Boot 默认支持 Session 功能。我们可以通过 HttpSession 对象来操作 Session。以下是一个简单的例子:
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class SessionController {
@GetMapping("/setSession")
public String setSession(HttpServletRequest request) {
HttpSession session = request.getSession(); // 获取 Session 对象,如果不存在则创建
session.setAttribute("userId", 12345); // 设置 Session 属性
return "Session 已设置";
}
@GetMapping("/getSession")
public String getSession(HttpServletRequest request) {
HttpSession session = request.getSession();
Integer userId = (Integer) session.getAttribute("userId"); // 获取 Session 属性
if (userId != null) {
return "Session 中的 userId:" + userId;
}
return "Session 中未找到 userId";
}
@GetMapping("/invalidateSession")
public String invalidateSession(HttpServletRequest request) {
HttpSession session = request.getSession(false); // 获取 Session 对象,如果不存在则不创建
if (session != null) {
session.invalidate(); // 销毁 Session
return "Session 已销毁";
}
return "Session 不存在";
}
}
Session 的集群问题
在分布式系统中,Session 的管理是一个复杂的问题。如果将 Session 存储在单台服务器上,当用户访问其他服务器时,就无法获取到 Session 信息。为了解决这个问题,可以使用以下方案:
- Session 复制:将 Session 复制到所有的服务器上。这种方案简单易用,但会占用大量的内存资源,并且存在数据同步的问题。
- 集中式 Session 管理:将 Session 存储在一个独立的服务器上,例如 Redis 或 Memcached。所有的服务器都从该服务器上获取 Session 信息。这种方案可以有效地解决 Session 的共享问题,但需要额外的服务器来存储 Session 数据,并且存在单点故障的风险。
- 基于 Cookie 的 Session:将 Session 信息存储在 Cookie 中。这种方案可以避免 Session 共享的问题,但 Cookie 的大小有限制,并且存在安全问题。
在实际应用中,我们通常会选择集中式 Session 管理方案,例如使用 Spring Session 来管理 Session。 Spring Session 提供了多种 Session 存储方案,例如 Redis、JDBC 等。使用 Spring Session,只需要简单的配置,就可以实现 Session 的共享。
Spring Session + Redis 实战
添加依赖

<dependency> <groupId>org.springframework.session</groupId> <artifactId>spring-session-data-redis</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>配置 Redis 连接信息
在
application.properties或application.yml中配置 Redis 连接信息:spring.redis.host=127.0.0.1 spring.redis.port=6379 # spring.redis.password=your_redis_password开启 Spring Session

在 Spring Boot 启动类上添加
@EnableRedisHttpSession注解:import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession; @SpringBootApplication @EnableRedisHttpSession // 开启 Spring Session public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } }配置完成后,Spring Boot 会自动将 Session 存储到 Redis 中。不同的服务器可以共享同一个 Session。
实战避坑经验总结
- Cookie 的大小限制:Cookie 的大小有限制,通常为 4KB。不要在 Cookie 中存储过多的数据。
- Session 的有效期:Session 的有效期可以通过
session.setMaxInactiveInterval()方法来设置。Session 过期后,服务器会自动销毁 Session 对象。 - Session 的并发问题:在并发环境下,多个线程可能会同时访问同一个 Session 对象。为了避免并发问题,可以使用同步机制,例如使用
synchronized关键字或者使用锁。 - 反向代理和 Cookie 的 Path 问题:使用 Nginx 等反向代理时,需要注意 Cookie 的 Path 配置,确保 Cookie 在整个域名下都有效。例如,如果你的应用部署在
/app目录下,需要将 Cookie 的 Path 设置为/app。 - Nginx 负载均衡和 Session 粘滞:在使用 Nginx 进行负载均衡时,需要配置 Session 粘滞(sticky session),确保同一个用户的请求被路由到同一台服务器上,避免 Session 丢失。可以使用 Nginx 的
ip_hash或least_conn策略来实现 Session 粘滞,或者使用 Spring Session 统一管理 Session。
总之,理解 Cookie 和 Session 的原理,合理地使用它们,可以帮助我们构建更加健壮和安全的应用。在分布式环境中,选择合适的 Session 管理方案至关重要,Spring Session + Redis 是一个不错的选择。
冠军资讯
代码一只喵