下面从架构、扩展性、安全性、管理成本等几个维度,对 **Session** 和 **JWT** 进行对比,帮助你根据场景选择合适的方案。 ------ ## 一、基本原理 | 特性 | Session | JWT(JSON Web Token) | | -------- | ------------------------------------ | ----------------------------------------------- | | 存储方式 | 服务端存储会话数据(如内存、Redis) | 客户端存储完整的令牌(通常在 Header 或 Cookie) | | 标识方式 | 客户端持有一个 Session ID | 客户端持有一个自包含的 Token | | 状态管理 | 有状态(Stateful),服务器要维护会话 | 无状态(Stateless),服务器不存会话 | ------ ## 二、对比分析 ### 1. 架构与扩展性 - **Session** - 单体应用:内存中维护 Map,简单易用。 - 分布式/集群:需要共享 Session(如 Redis、数据库、Sticky Session),增加运维成本。 - **JWT** - 无状态:令牌自带用户信息及签名,服务器只需校验签名即可,无需存储。 - 分布式友好:各节点只要共享签名密钥(或公钥),即可校验,无需集中存储。 ### 2. 性能 - **Session** - 每次请求都要从存储(内存/Redis/DB)读取会话数据,IO 成本或网络开销。 - 并发高时,集中式 Session 存储可能成为瓶颈。 - **JWT** - 校验签名(HMAC 或 RSA)为 CPU 操作,无网络开销,性能开销较小。 - 但 Token 通常更大(包含多段 Base64),每次请求都要传输,带宽略增。 ### 3. 安全性 - **Session** - 会话数据保存在服务器端,客户端只能拿到 Session ID,敏感数据不暴露。 - 可在服务器端随时销毁或更新 Session(强制登出、权限变更即时生效)。 - **JWT** - 令牌自包含所有声明(claims),如果存敏感数据需加密(JWE),否则仅签名(JWS)也可能泄露信息。 - 无法主动撤销(除非做黑名单),需要控制有效期并结合“刷新令牌”机制。 ### 4. 可控性与管理 - **Session** - **可控性强**:服务器可随时作废 Session,适合需要即时注销、权限动态调整的场景。 - 过期策略灵活:可按用户、按应用统一配置。 - **JWT** - **可控性弱**:Token 一旦签发,在到期前无法从服务器强制失效(除非额外维护黑名单)。 - 需要设计“短生命周期 + 刷新令牌”模式,增加实现复杂度。 ### 5. 跨域与移动端 - **Session** - 依赖 Cookie(同源策略),跨域或移动端(原生 App)使用受限。 - 跨域时需配合 CORS + `withCredentials`,且浏览器必须支持并开启 Cookie。 - **JWT** - 与 HTTP 协议无关,既可放在 Authorization 头,也可放在 URL、LocalStorage,移动端/第三方客户端更友好。 - 只要客户端能发送 HTTP Header,就能携带 Token。 ### 6. 实现复杂度 - **Session** - 框架通常开箱即用(如 Spring Session),开发者只需开启即可。 - 自动处理过期、失效,管理简单。 - **JWT** - 需要设计签名算法、密钥管理、过期策略、刷新机制、黑名单等。 - 容易因配置不当造成安全漏洞(算法降级、密钥泄露、Token 劫持等)。 ------ ## 三、何时选用 | 场景类型 | 推荐方案 | 原因 | | --------------------------- | -------- | ---------------------------------------------------------- | | 单体 Web 应用、后台管理系统 | Session | 简单、可控、安全性高,框架支持完善。 | | 分布式微服务、无状态 API | JWT | 无需集中存储,易扩展;移动端/第三方客户端友好。 | | 高度安全、需即时失效场景 | Session | 可随时在服务器端销毁会话,确保强制登出或权限变更即时生效。 | | 跨域或多端(Web + App) | JWT | Token 可在多种客户端轻松传递,无需依赖浏览器 Cookie。 | ------ ## 四、示例代码 ### Session 示例(Spring Boot) ```java // 登录时创建 Session @PostMapping("/login") public String login(HttpServletRequest req, @RequestParam String user, @RequestParam String pass) { if (authService.verify(user, pass)) { req.getSession().setAttribute("userId", user); return "登录成功"; } return "登录失败"; } // 受保护资源 @GetMapping("/profile") public User profile(HttpServletRequest req) { String userId = (String) req.getSession().getAttribute("userId"); return userService.findById(userId); } ``` ### JWT 示例(Spring Boot + jjwt) ```java // 生成 Token @PostMapping("/login") public String login(@RequestParam String user, @RequestParam String pass) { if (authService.verify(user, pass)) { return Jwts.builder() .setSubject(user) .setExpiration(new Date(System.currentTimeMillis() + 3600_000)) .signWith(SignatureAlgorithm.HS256, secretKey) .compact(); } throw new UnauthorizedException(); } // 过滤器中校验 @Override protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain chain) { String token = req.getHeader("Authorization"); if (token != null && token.startsWith("Bearer ")) { String user = Jwts.parser().setSigningKey(secretKey) .parseClaimsJws(token.substring(7)) .getBody().getSubject(); // 将 user 存入 SecurityContext … } chain.doFilter(req, res); } ``` ------ ## 五、结论 - **Session**:上手简单、安全可控,适合绝大多数传统 Web 应用。 - **JWT**:更灵活、易扩展,适合分布式架构、多端场景,但需要更复杂的设计与安全防护。 根据你的项目架构、团队经验和安全需求,选择最合适的方案即可。