三者并非同一层级的技术概念,核心定位差异决定了它们在 Token 生成逻辑、设计理念和使用方式上的本质区别:
- JWT 是一种令牌格式标准,仅规定 Token 的结构与签名规则
- Sa-Token 是全功能权限认证框架,内置 Token 生成、校验、权限管控全套能力
- Spring Security 是安全管控框架,核心负责认证授权流程,本身不提供开箱即用的业务 Token 生成能力
一、各自的 Token 生成机制
1. JWT(JSON Web Token)
- 本质:RFC 7519 定义的开放令牌标准,是通用格式规范而非框架
- 生成逻辑 :
- 构造 Header:声明签名算法(如 HS256、RS256)与 Token 类型
- 构造 Payload:存放用户 ID、过期时间、角色权限等自定义声明
- 生成 Signature:用密钥 + 指定算法,对 Header、Payload 的 Base64 编码结果签名
- 最终 Token 格式:
Base64(Header).Base64(Payload).签名
- 核心特征 :无状态,Token 自身携带所有用户信息,服务端无需存储,仅验证签名合法性即可解析内容。
2. Sa-Token
- 本质:国产 Java 权限认证框架,Token 生成与管理是其内置核心能力
- 默认生成逻辑(有状态模式) :
- 生成随机字符串作为 Token 值,默认 UUID 风格,内置 simple-uuid、random-32 等多种样式,也支持自定义生成策略
- 将 Token 与用户 ID、会话数据(权限、过期时间等)绑定,存储在服务端(默认内存,可一键切换 Redis 实现分布式会话共享)
- 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 实现 |
| 开发成本 | 中等,需处理验签、异常、续签 | 极低,开箱即用 | 高,配置与自定义逻辑繁琐 |
三、选型参考
- 快速开发、中小型项目:优先选 Sa-Token,Token 管理、权限校验全封装,开发效率最高
- 微服务、跨系统鉴权:优先采用 JWT 标准,天然适配无状态分布式架构
- 大型企业级项目、复杂安全诉求:选 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:核心优势是高度灵活、生态完善,适合大型企业级项目、复杂安全规则场景。