sa-token,jwt,security 生成 token 的区别

三者并非同一层级的技术概念,核心定位差异决定了它们在 Token 生成逻辑、设计理念和使用方式上的本质区别:

  • JWT 是一种令牌格式标准,仅规定 Token 的结构与签名规则
  • Sa-Token全功能权限认证框架,内置 Token 生成、校验、权限管控全套能力
  • Spring Security安全管控框架,核心负责认证授权流程,本身不提供开箱即用的业务 Token 生成能力

一、各自的 Token 生成机制

1. JWT(JSON Web Token)
  • 本质:RFC 7519 定义的开放令牌标准,是通用格式规范而非框架
  • 生成逻辑
    1. 构造 Header:声明签名算法(如 HS256、RS256)与 Token 类型
    2. 构造 Payload:存放用户 ID、过期时间、角色权限等自定义声明
    3. 生成 Signature:用密钥 + 指定算法,对 Header、Payload 的 Base64 编码结果签名
    4. 最终 Token 格式:Base64(Header).Base64(Payload).签名
  • 核心特征无状态,Token 自身携带所有用户信息,服务端无需存储,仅验证签名合法性即可解析内容。
2. Sa-Token
  • 本质:国产 Java 权限认证框架,Token 生成与管理是其内置核心能力
  • 默认生成逻辑(有状态模式)
    1. 生成随机字符串作为 Token 值,默认 UUID 风格,内置 simple-uuid、random-32 等多种样式,也支持自定义生成策略
    2. 将 Token 与用户 ID、会话数据(权限、过期时间等)绑定,存储在服务端(默认内存,可一键切换 Redis 实现分布式会话共享)
    3. Token 本身仅作为会话标识,不携带业务信息,校验时必须查询服务端存储
  • 扩展能力:官方提供 JWT 插件,支持切换为 JWT 格式 Token,提供 Simple、Mixin、Stateless 三种模式,可兼顾无状态特性与服务端管控能力。
3. Spring Security
  • 本质 :Spring 生态安全框架,原生不提供业务身份 Token 生成能力
  • 原生机制:默认基于 HttpSession 实现会话管理,仅自动生成 CSRF Token 用于防跨站攻击,并非用户身份认证 Token
  • Token 生成实现方式
    • 需开发者手动集成第三方 JWT 库(如 jjwt、auth0 java-jwt),在认证成功后自行编写代码生成 Token
    • 或集成 Spring Security OAuth2 授权服务器模块,通过标准 OAuth2 流程生成令牌(支持 JWT、不透明令牌等格式)
  • 核心特征:框架只管控认证流程与权限校验,Token 的生成规则、存储、续签逻辑全部由开发者自定义,灵活度高但编码量大。

二、核心差异对比

表格

对比维度 JWT Sa-Token(默认模式) Spring Security
定位层级 令牌格式标准 全栈权限认证框架 安全流程管控框架
Token 结构 三段式固定结构,自包含用户信息 随机字符串,仅作会话 ID 无固定格式,完全由开发者自定义
信息存储 全部存在 Token 中,服务端不存储 存在服务端(内存 / Redis),Token 只做映射 由开发者决定(通常存 Redis 或 JWT 自包含)
状态类型 无状态 有状态 原生有状态(Session),扩展后可无状态
生成复杂度 需自行实现工具类、签名逻辑 框架内置,一行代码生成 需手动集成扩展,编码量最大
主动失效 困难,需依赖黑名单或短有效期 原生支持,可随时踢人、注销、封禁 需自行实现
续签机制 需手动实现刷新令牌逻辑 内置 active-timeout 自动续签 需自行编码实现
集群适配 天然支持水平扩展 切换 Redis 即可支持 需配合 Redis 或 JWT 实现
开发成本 中等,需处理验签、异常、续签 极低,开箱即用 高,配置与自定义逻辑繁琐

三、选型参考

  1. 快速开发、中小型项目:优先选 Sa-Token,Token 管理、权限校验全封装,开发效率最高
  2. 微服务、跨系统鉴权:优先采用 JWT 标准,天然适配无状态分布式架构
  3. 大型企业级项目、复杂安全诉求:选 Spring Security + JWT/OAuth2 组合,灵活度最高,生态最完善

一、三者 Token 生成代码示例

1. JWT(基于 jjwt 工具库)

JWT 是通用令牌标准,通常借助第三方工具库手动实现,与任何业务框架无关。

Maven 依赖

java 复制代码
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-api</artifactId>
    <version>0.11.5</version>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-impl</artifactId>
    <version>0.11.5</version>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-jackson</artifactId>
    <version>0.11.5</version>
    <scope>runtime</scope>
</dependency>

核心工具类

java 复制代码
import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

public class JwtUtils {
    // HS256 要求密钥长度 ≥ 256 位,生产环境需加密配置、禁止硬编码
    private static final String SECRET_KEY = "your_secure_secret_key_min_256_bits_length";
    // Token 有效期:2小时
    private static final long EXPIRE_MS = 2 * 60 * 60 * 1000L;

    /**
     * 生成 JWT Token
     */
    public static String generateToken(Long userId, String username) {
        Map<String, Object> claims = new HashMap<>();
        claims.put("userId", userId);
        claims.put("username", username);

        return Jwts.builder()
                .setClaims(claims)
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + EXPIRE_MS))
                .signWith(
                    Keys.hmacShaKeyFor(SECRET_KEY.getBytes(StandardCharsets.UTF_8)),
                    SignatureAlgorithm.HS256
                )
                .compact();
    }

    /**
     * 解析 Token,获取载荷内容
     */
    public static Claims parseToken(String token) {
        return Jwts.parserBuilder()
                .setSigningKey(Keys.hmacShaKeyFor(SECRET_KEY.getBytes(StandardCharsets.UTF_8)))
                .build()
                .parseClaimsJws(token)
                .getBody();
    }

    /**
     * 校验 Token 合法性
     */
    public static boolean validateToken(String token) {
        try {
            parseToken(token);
            return true;
        } catch (JwtException | IllegalArgumentException e) {
            // 签名错误、过期、格式非法等均视为无效
            return false;
        }
    }
}

2. Sa-Token 框架

Sa-Token 内置全套 Token 生成、存储、校验、管控能力,无需手动实现工具类,一行代码即可完成登录发证。

Maven 依赖

java 复制代码
<dependency>
    <groupId>cn.dev33</groupId>
    <artifactId>sa-token-spring-boot-starter</artifactId>
    <version>1.37.0</version>
</dependency>

配置文件(application.yml)

java 复制代码
sa-token:
  token-name: satoken        # Token 请求头/ Cookie 名称
  timeout: 2592000          # Token 总有效期,单位秒(默认 30 天)
  active-timeout: 1800      # 临时有效期,无操作自动过期(自动续签)
  token-style: uuid         # Token 生成样式:uuid / random-32 / random-64
  is-concurrent: true       # 是否允许同账号多端同时登录

业务代码示例

java 复制代码
import cn.dev33.satoken.stp.StpUtil;
import org.springframework.web.bind.annotation.*;

@RestController
public class LoginController {

    // 登录并生成 Token
    @PostMapping("/login")
    public String login(String username, String password) {
        // 1. 自行完成账号密码校验
        if ("admin".equals(username) && "123456".equals(password)) {
            // 2. 框架自动生成随机 Token、创建会话、写入服务端存储
            StpUtil.login(10001);
            // 3. 直接获取生成好的 Token 返回前端
            return "登录成功,Token:" + StpUtil.getTokenValue();
        }
        return "账号密码错误";
    }

    // 主动注销当前 Token
    @PostMapping("/logout")
    public String logout() {
        StpUtil.logout();
        return "注销成功";
    }

    // 服务端强制踢人下线,Token 立即失效
    @PostMapping("/kickout")
    public String kickout(Long userId) {
        StpUtil.kickout(userId);
        return "用户已被强制下线";
    }
}

说明:默认使用内存存储,引入 sa-token-redis 依赖即可无缝切换为分布式存储,业务代码无需修改。


3. Spring Security + JWT

Spring Security 原生不提供业务身份 Token 生成能力,需手动集成 JWT 库并嵌入认证流程,开发量最大、灵活度最高。

Maven 依赖

java 复制代码
<!-- Spring Security 核心 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- 额外引入 jjwt 依赖,同上面 JWT 部分 -->

核心组件 1:登录成功处理器(生成 Token)

java 复制代码
@Component
public class JwtLoginSuccessHandler implements AuthenticationSuccessHandler {
    @Override
    public void onAuthenticationSuccess(HttpServletRequest request,
                                        HttpServletResponse response,
                                        Authentication authentication) throws IOException {
        // 获取认证通过的用户信息
        String username = authentication.getName();
        // 调用 JWT 工具类生成 Token(工具类同上)
        String token = JwtUtils.generateToken(10001L, username);

        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().write("{\"code\":200,\"token\":\"" + token + "\"}");
    }
}

核心组件 2:JWT 认证过滤器(校验 Token)

java 复制代码
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain filterChain) throws ServletException, IOException {
        // 从请求头获取 Token
        String token = request.getHeader("Authorization");

        if (token != null && JwtUtils.validateToken(token)) {
            // 解析载荷,封装权限信息
            Claims claims = JwtUtils.parseToken(token);
            String username = claims.get("username", String.class);
            
            // 注入 Security 上下文,完成本次请求认证
            UsernamePasswordAuthenticationToken authToken =
                new UsernamePasswordAuthenticationToken(username, null,
                    AuthorityUtils.createAuthorityList("ROLE_admin"));
            SecurityContextHolder.getContext().setAuthentication(authToken);
        }
        filterChain.doFilter(request, response);
    }
}

核心组件 3:Security 配置类

java 复制代码
@Configuration
@EnableWebSecurity
public class SecurityConfig {
    @Autowired
    private JwtLoginSuccessHandler successHandler;
    @Autowired
    private JwtAuthenticationFilter jwtFilter;

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .csrf().disable()
            .authorizeHttpRequests()
                .antMatchers("/login").permitAll()
                .anyRequest().authenticated()
            .and()
            .formLogin()
                .successHandler(successHandler)   // 登录成功后触发 JWT 生成
            .and()
            // 将 JWT 过滤器加入认证链路
            .addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class);
        return http.build();
    }
}

二、安全性深度对比

对比维度 JWT 标准 Sa-Token(默认模式) Spring Security + JWT
防篡改能力 依赖签名算法保证,密钥泄露可全局伪造 Token Token 为随机无意义字符串,天然防篡改 与 JWT 一致
信息泄露风险 高,Payload 仅 Base64 编码,可直接解码查看 低,Token 不携带业务数据,信息全在服务端 与 JWT 一致
主动失效能力 弱,需依赖黑名单或短有效期,实时性差 极强,注销、踢人、封禁一行代码即时生效 弱,需自行实现黑名单逻辑
盗用防护能力 弱,无状态无法识别盗用,需额外绑定 IP/UA 强,内置互斥登录、IP 绑定、设备绑定、异常检测 弱,需全部自行实现
核心安全依赖 依赖密钥安全性,密钥泄露全局失控 依赖服务端存储(Redis / 内存)的安全性 依赖密钥与自行实现的防护逻辑
CSRF 风险 Header 传递时天然免疫 Cookie 模式存在风险,Header 模式免疫 原生支持 CSRF 防护,可手动关闭
XSS 风险 前端存储均有被盗风险,HttpOnly Cookie 可缓解 同左 同左

补充选型总结

  • JWT:核心优势是无状态、跨语言跨平台通用,适合微服务、跨系统鉴权、第三方授权场景;
  • Sa-Token:核心优势是开箱即用、会话管控能力强,适合单体、中小型项目快速落地;
  • Spring Security:核心优势是高度灵活、生态完善,适合大型企业级项目、复杂安全规则场景。
相关推荐
孔明click334 天前
Sa-Token v1.46.0 发布 🚀,来看看有没有令你心动的功能!
java·sa-token·开源·springboot·权限认证
FungLeo10 天前
Node 后端实战 · JWT 双密钥轮转与 token 版本号:多租户 SaaS 如何不停机换密钥、一键踢全设备
jwt·长短 token·无感刷新 token·jose·hs256
夜雨风云19 天前
系统或软件的安全(Security)
安全·security·软件设计·软件架构设计·服务质量模型
论迹复利22 天前
第 2 章 PSA Certified 四级认证体系
物联网·安全·security·cra·psa
学编程就要猛23 天前
解析博客系统后端实现
java·mysql·jwt·摘要算法·加盐
介一安全1 个月前
【SRC】基础思路篇15:JWT安全测试完全指南
web安全·jwt·安全性测试·src
是小蟹呀^1 个月前
Spring Security + JWT 面试题整理
java·jwt·springsecurity
向日的葵0061 个月前
Redis会话机制vsJWT机制深度解析
数据库·redis·python·缓存·系统架构·jwt
xiaofeichaichai1 个月前
NestJS 实战:JWT 登录认证、Token 刷新与安全退出
redis·nestjs·jwt·twitter