JavaWeb中Cookie与Session详解:从原理到实战

228 阅读4分钟

JavaWeb中Cookie与Session详解:从原理到实战

作为后端Java工程师,处理用户会话管理是Web开发的核心技能之一。本文将深入剖析Cookie和Session的底层原理、差异对比,并结合Spring Boot实现完整的用户登录案例,帮助开发者彻底掌握这一核心知识点。


一、Cookie详解

1. Cookie本质与工作原理

  • 定义:Cookie是服务器发送到客户端(浏览器)的小型文本文件,存储在客户端本地。

  • 工作流

    1. 服务器通过Set-Cookie响应头设置Cookie
    2. 浏览器保存Cookie并在后续请求中通过Cookie请求头携带
    3. 服务器解析Cookie完成会话跟踪

2. Cookie核心属性

属性名作用示例值
Name=Value键值对存储数据userId=123
Domain限定Cookie生效的域名.example.com
Path限定Cookie生效的路径/api
Expires/Max-Age过期时间(绝对时间/相对秒数)Wed, 21 Oct 2025 07:28:00 GMT
Secure仅通过HTTPS传输Secure
HttpOnly禁止JavaScript访问,防止XSS攻击HttpOnly
SameSite防止CSRF攻击(Strict/Lax/None)SameSite=Lax

3. Java原生Cookie操作

	// 创建Cookie
	Cookie cookie = new Cookie("theme", "dark");
	cookie.setPath("/");
	cookie.setMaxAge(7 * 24 * 60 * 60); // 7天
	cookie.setHttpOnly(true);
	cookie.setSecure(true); // 仅HTTPS生效
	// 添加到响应
	response.addCookie(cookie);
	// 读取Cookie
	Cookie[] cookies = request.getCookies();

	if (cookies != null) {

	    for (Cookie c : cookies) {

	        if ("theme".equals(c.getName())) {

	            System.out.println("用户主题偏好: " + c.getValue());

	        }

	    }

	}

二、Session详解

1. Session本质与工作原理

  • 定义:Session是服务器端维护的会话状态容器,通过唯一标识符(Session ID)关联客户端请求。

  • 工作流

    1. 客户端首次请求时,服务器生成Session ID
    2. 通过Cookie(默认)或URL重写将Session ID返回客户端
    3. 后续请求携带Session ID,服务器恢复会话状态

2. Session实现机制

  • 存储方式

    • 内存存储:默认实现(如Tomcat的StandardManager
    • 分布式存储:Redis(Spring Session集成)
    • 数据库存储:JDBC实现
  • 生命周期

    • 创建:request.getSession()首次调用时
    • 销毁:超时(默认30分钟)、调用invalidate()、服务器重启

3. Spring Boot集成Session

	@RestController

	@RequestMapping("/api/auth")

	public class AuthController {
	    @PostMapping("/login")

	    public ResponseEntity<String> login(@RequestParam String username, 

	                                       @RequestParam String password,

	                                       HttpSession session) {

	        // 模拟认证

	        if ("admin".equals(username) && "123456".equals(password)) {

	            // 存储用户信息到Session

	            session.setAttribute("user", User.builder()

	                    .id(1L)

	                    .username(username)

	                    .loginTime(LocalDateTime.now())

	                    .build());

	            // 设置Session超时时间(秒)

	            session.setMaxInactiveInterval(1800); // 30分钟

	            return ResponseEntity.ok("登录成功");

	        }

	        return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("用户名或密码错误");

	    }

	    @GetMapping("/profile")
	    public ResponseEntity<User> getProfile(HttpSession session) {

	        User user = (User) session.getAttribute("user");

	        if (user != null) {

	            return ResponseEntity.ok(user);
	        }

	        return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
	    }
	    @GetMapping("/logout")

	    public ResponseEntity<String> logout(HttpSession session) {

	        session.invalidate();

	        return ResponseEntity.ok("已退出登录");

	    }

	}
	// 用户实体类

	@Data
	@Builder

	@AllArgsConstructor

	@NoArgsConstructor

	public class User {

	    private Long id;

	    private String username;

	    private LocalDateTime loginTime;

	}

三、Cookie vs Session深度对比

特性CookieSession
存储位置客户端浏览器服务器端
数据大小限制单个Cookie ≤4KB,总数有限制理论上无限制(受服务器内存/存储影响)
安全性较低(可被篡改,需配合HttpOnly/Secure)较高(存储在服务端)
跨域支持需配置Domain属性天然支持跨域(通过Session共享)
典型应用场景记住用户偏好、购物车用户登录状态、购物车(敏感数据)

四、实战案例:基于Session的用户登录系统

1. 系统架构

  • 前端:HTML表单提交用户名/密码
  • 后端:Spring Boot处理认证逻辑
  • Session管理:存储用户登录状态
  • 安全增强:CSRF防护、HTTPS、Session固定攻击防护

2. 完整代码实现

2.1 配置类(启用CSRF防护)
	@Configuration
	@EnableWebSecurity
	public class SecurityConfig extends WebSecurityConfigurerAdapter {
	    @Override
	    protected void configure(HttpSecurity http) throws Exception {
	        http
	            .authorizeRequests()

	                .antMatchers("/login", "/css/**", "/js/**").permitAll()

	                .anyRequest().authenticated()

	            .and()

	            .formLogin()

	                .loginPage("/login")

	                .defaultSuccessUrl("/dashboard")

	                .permitAll()

	            .and()

	            .logout()

	                .permitAll()

	            .and()

	            .csrf().disable(); // 开发环境禁用,生产环境需配置CSRF令牌

	    }

	}
2.2 控制器
	@Controller
	public class LoginController {
	    @GetMapping("/login")

	    public String showLoginForm() {

	        return "login";

	    }

	    @PostMapping("/login")
	    public String processLogin(@RequestParam String username,
	                              @RequestParam String password,
	                              HttpSession session,
	                              RedirectAttributes redirectAttributes) {

	        // 模拟认证
	        if ("admin".equals(username) && "123456".equals(password)) {

	            session.setAttribute("user", User.builder()
	                    .id(1L)

	                    .username(username)

	                    .build());

	            return "redirect:/dashboard";

	        }

	        redirectAttributes.addFlashAttribute("error", "用户名或密码错误");

	        return "redirect:/login";

	    }
	    @GetMapping("/dashboard")
	    public String showDashboard(HttpSession session, Model model) {

	        User user = (User) session.getAttribute("user");

	        if (user == null) {

	            return "redirect:/login";
	        }

	        model.addAttribute("user", user);

	        return "dashboard";

	    }
	    @GetMapping("/logout")
	    public String logout(HttpSession session) {

	        session.invalidate();

	        return "redirect:/login";

	    }

	}
2.3 Thymeleaf模板(login.html)
	<!DOCTYPE html>

	<html xmlns:th="http://www.thymeleaf.org">

	<head>

	    <title>用户登录</title>

	</head>

	<body>

	    <div th:if="${error}" class="alert alert-danger">

	        <span th:text="${error}">错误信息</span>

	    </div>

	    <form method="post" th:action="@{/login}">

	        <div>

	            <label>用户名:</label>

	            <input type="text" name="username" required>

	        </div>

	        <div>

	            <label>密码:</label>

	            <input type="password" name="password" required>

	        </div>

	        <button type="submit">登录</button>

	    </form>

	</body>

	</html>

五、生产环境最佳实践

  1. Cookie安全增强

    	// 强制使用Secure和HttpOnly
    
    	cookie.setSecure(true);
    
    	cookie.setHttpOnly(true);
    
    	// 防止CSRF攻击
    
    	cookie.setSameSite("Strict");
    
  2. Session分布式部署

    	// Spring Session集成Redis
    
    	@EnableRedisHttpSession
    
    	public class RedisSessionConfig {
    
    	    @Bean
    
    	    public LettuceConnectionFactory connectionFactory() {
    
    	        return new LettuceConnectionFactory();
    
    	    }
    
    	}
    
  3. Session固定攻击防护

    	// 登录成功后重新生成Session ID
    
    	@PostMapping("/login")
    
    	public String login(...) {
    
    	    // ...认证逻辑...
    
    	    HttpSession session = request.getSession(false);
    
    	    if (session != null) {
    
    	        session.invalidate(); // 销毁旧Session
    
    	    }
    
    	    session = request.getSession(true); // 创建新Session
    
    	    session.setAttribute("user", user);
    
    	    // ...
    
    	}
    
  4. 性能优化

    • 合理设置Session超时时间
    • 敏感数据存储在服务端,非敏感数据可存储在加密的Cookie中
    • 使用Token(如JWT)替代Session实现无状态认证

六、总结

Cookie和Session是Web会话管理的两大基石,开发者需根据业务场景选择合适方案:

  • 使用Cookie:存储非敏感数据(如主题偏好)、实现"记住我"功能
  • 使用Session:存储用户登录状态、购物车等敏感数据