Session、Token 和 JWT 的区别对比

Session、Token 和 JWT 的区别对比

本文由AI生成:

1. 数据存储位置

Session (HttpSession)

  • 数据存储在服务器内存中
  • 客户端只保存 sessionId 标识符
  • 示例:session.setAttribute(SESSION_KEY,msg) 将数据存储在服务端

普通 Token (Redis存储)

  • 数据存储在外部存储(如 Redis)中
  • 客户端保存 token 标识符
  • 示例:redisUtils.set(token,msg) 将数据存储在 Redis 中

JWT

  • 数据编码在 token 本身中,存储在客户端
  • 服务端不存储任何会话数据
  • 示例:jwtUtil.createToken(JWT_KEY,msg,10000) 生成包含数据的完整 token

2. 状态管理方式

Session

  • 有状态:服务端必须维护会话状态
  • 依赖服务器内存或持久化存储

普通 Token

  • 有状态:服务端需维护 token 与数据的映射关系
  • 依赖外部存储系统

JWT

  • 无状态:服务端不保存任何会话信息
  • 自包含:所有必要信息都在 token 中

3. 扩展性和分布式支持

Session

  • 扩展性差:多服务器部署时需要会话复制或粘性会话
  • 难以实现真正的负载均衡

普通 Token

  • 扩展性好:通过共享存储(如 Redis 集群)实现分布式支持
  • 支持水平扩展

JWT

  • 扩展性最佳:天然支持分布式无状态服务
  • 无需共享存储即可实现会话共享

4. 安全性考虑

Session

  • 安全性依赖 sessionId 的保密性
  • 需要防范 Session Fixation 攻击

普通 Token

  • 安全性依赖 token 的随机性和保密性
  • 需要防范 token 泄露和重放攻击

JWT

  • 安全性依赖签名算法和密钥保护
  • 需要防范 token 泄露,但无法中途撤销(除非设置黑名单)

5. 性能特征

Session

  • 服务端内存消耗较大
  • 访问速度快(内存访问)

普通 Token

  • 服务端内存消耗小
  • 需要网络访问外部存储(如 Redis)

JWT

  • 服务端零内存消耗
  • 需要 CPU 进行加解密运算
  • 网络传输数据量相对较大

6. 生命周期管理

Session

  • 服务端可主动销毁会话
  • 支持精确的会话过期控制

普通 Token

  • 服务端可主动删除 token
  • 支持精确的过期时间控制

JWT

  • 一旦签发难以中途撤销
  • 过期时间编码在 token 中,无法动态调整

7. 适用场景

Session

  • 传统的 Web 应用
  • 用户量适中且不需要分布式部署的系统
  • 需要存储大量会话数据的场景

普通 Token

  • 分布式系统架构
  • 需要在多个服务间共享认证状态
  • 移动端应用后端

JWT

  • 微服务架构
  • 单点登录(SSO)系统
  • API 认证和授权
  • 跨域认证需求

8. 实现复杂度

Session

  • 实现简单,Servlet 容器内置支持

普通 Token

  • 需要引入外部存储组件

JWT

  • 需要实现 token 的生成和验证逻辑
java 复制代码
package com.easysession.controller;

import com.easysession.gwt.JWTUtil;
import com.easysession.redis.RedisUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.UUID;

/**
 * @ClassName TestController
 * @Description TODO
 * @Author Administrator
 * @Date 2023/12/16 16:22
 */
@RestController
public class TestController {

    private static final String SESSION_KEY="session_key";

    private static final String JWT_KEY ="jwt_key";

    @Resource
    private RedisUtils redisUtils;

    @Resource
    private JWTUtil<String> jwtUtil;

    private boolean isEmpty(String value){
        if(null==value||"".equals(value.trim())){
            return true;
        }
        return false;
    }

    @RequestMapping("saveSessionInfo")
    public String saveSessionInfo(HttpSession session,String msg){
        if(isEmpty(msg)){
            return "msg不能为空";
        }
        session.setAttribute(SESSION_KEY,msg);
        return "保存session信息成功,sessionId:"+session.getId();
    }

    @RequestMapping("getSessionInfo")
    public String getSessionInfo(HttpSession session){
        return "获取的session信息为:"+session.getAttribute(SESSION_KEY);
    }

    @RequestMapping("saveByToken")
    public String saveByToken(String msg){
        if(isEmpty(msg)){
            return "msg不能为空";
        }
        String token = UUID.randomUUID().toString();
        redisUtils.set(token,msg);
        return "保存token信息成功,token:"+token;
    }

    @RequestMapping("getByToken")
    public String getByToken(String token){
        if(isEmpty(token)){
            return "token不能为空";
        }
        return "获取的token信息为:"+redisUtils.get(token);
    }

    @RequestMapping("saveByTokenWithCookie")
    public String saveByTokenWithCookie(HttpServletResponse response,String msg){
        if(isEmpty(msg)){
            return "msg不能为空";
        }
        String token = UUID.randomUUID().toString();
        redisUtils.set(token,msg);
        Cookie cookie = new Cookie("token",token);
        //cookie.setMaxAge(10);
        response.addCookie(cookie);
        return "保存token信息成功,token:"+token;
    }

    @RequestMapping("getByTokenWithCookie")
    public String getByTokenWithCookie(HttpServletRequest request){
      Cookie[] cookies =   request.getCookies();
      if(cookies==null){
          return  "获取的token信息为:null";
      }
      String token = null;
      for (Cookie cookie:cookies){
          if("token".equals(cookie.getName())){
              token = cookie.getValue();
              break;
          }
      }
        if(isEmpty(token)){
            return "token不能为空";
        }
        return  "获取的token信息为:"+redisUtils.get(token);
    }

    @RequestMapping("saveByJwt")
    public String saveByJwt(String msg){
        if(isEmpty(msg)){
            return "msg不能为空";
        }
        String token =jwtUtil.createToken(JWT_KEY,msg,10000);
        return "保存token信息成功,token:"+token;
    }


    @RequestMapping("getByJwt")
    public String getByJwt(String token){
        if(isEmpty(token)){
            return "token不能为空";
        }
        String msg =jwtUtil.getTokenData(JWT_KEY,token,String.class);
        return "获取jwt的信息是"+msg;
    }
}
java 复制代码
package com.easysession.gwt;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import org.springframework.stereotype.Component;

import java.util.Date;

@Component("jwtUtil")
public class JWTUtil<T> {

    private static final String SECRET = "test123456";

    /**
     * 签名生成
     *
     * @return
     */
    public String createToken(String key, T data, Integer expireSeconds) {
        String token = null;
        try {
            Date expiresAt = new Date(System.currentTimeMillis() + expireSeconds * 1000);
            token = JWT.create()
                    .withClaim(key, JsonUtils.convertObj2Json(data))
                    .withExpiresAt(expiresAt)
                    .sign(Algorithm.HMAC256(SECRET));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return token;
    }

    /**
     * 签名验证
     *
     * @param token
     * @return
     */
    public <T> T getTokenData(String key, String token, Class<T> classz) {
        try {
            if (null == token || "".equals(token)) {
                return null;
            }
            JWTVerifier verifier = JWT.require(Algorithm.HMAC256(SECRET)).build();
            DecodedJWT jwt = verifier.verify(token);
            String jsonData = jwt.getClaim(key).asString();
            return JsonUtils.convertJson2Obj(jsonData, classz);
        } catch (Exception e) {
            return null;
        }
    }
}
相关推荐
Thomas_YXQ2 小时前
Unity3D的委托和事件的用法详解
java·开发语言
zwxu_2 小时前
thread堆栈分析报告
java·微服务·消息队列·熔断
百***78752 小时前
gpt-image-1.5极速接入指南:3步上手+图像核心能力解析+避坑手册
android·java·gpt
阿蒙Amon2 小时前
C#每日面试题-值类型与引用类型区别
java·面试·c#
nnsix2 小时前
Unity SenseGlove力反馈手套 基础配置
java·unity·游戏引擎
用户9446814013502 小时前
JUC 小试牛刀:从源码分析「ArrayBlockingQueue」,Java自带的线程安全的、有界的阻塞队列
java·后端
百***24372 小时前
GPT-Image 1.5 vs Nano Banana Pro 深度对比:国内业务落地的场景适配与避坑指南
java·数据库·gpt
代码栈上的思考2 小时前
MyBatis——动态SQL讲解
java·开发语言·数据库
@淡 定2 小时前
JVM调优参数配置详解
java·jvm·算法