当前位置: 首页 > news >正文

jwt与token+redis,哪种方案更好用?

前言

今天我们来聊聊一个非常经典的话题:JWT和Token+Redis两种认证方案,到底哪种更好用?

有些小伙伴在工作中可能会纠结于选择哪种方案,今天我就从底层原理到实际应用,给大家做一个全面的剖析。

希望对你会有所帮助。

一、认证与授权

在深入讨论之前,我们先明确两个基本概念:

  1. 认证(Authentication):你是谁?验证用户身份的过程

  2. 授权(Authorization):你能做什么?验证用户权限的过程

无论是JWT还是Token+Redis,都是用来解决这两个问题的技术方案。

二、JWT方案

2.1 JWT是什么?

JWT(JSON Web Token)是一种开放标准(RFC 7519),用于在各方之间安全地传输信息作为JSON对象。JWT由三部分组成:

header.payload.signature
  • Header:包含令牌类型和签名算法

  • Payload:包含声明(用户信息、过期时间等)

  • Signature:用于验证消息在传输过程中没有被篡改

2.2 JWT的工作流程

让我们通过一个完整的登录流程来理解JWT的工作原理:

2.3 JWT的Java实现示例

下面是一个简单的JWT工具类实现:

import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import java.security.Key;
import java.util.Date;public class JwtUtil {// 密钥,实际项目中应从配置中读取private static final Key key = Keys.secretKeyFor(SignatureAlgorithm.HS256);// 过期时间:2小时private static final long EXPIRATION_TIME = 2 * 60 * 60 * 1000;/*** 生成JWT*/public static String generateToken(String userId, String username, List<String> roles) {return Jwts.builder().setSubject(userId).claim("username", username).claim("roles", roles).setIssuedAt(new Date()).setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME)).signWith(key).compact();}/*** 验证并解析JWT*/public static Claims parseToken(String token) {try {return Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token).getBody();} catch (ExpiredJwtException e) {thrownew RuntimeException("Token已过期", e);} catch (JwtException e) {thrownew RuntimeException("Token无效", e);}}/*** 刷新Token*/public static String refreshToken(String token) {Claims claims = parseToken(token);return generateToken(claims.getSubject(), claims.get("username", String.class), claims.get("roles", List.class));}
}

2.4 JWT的优点和缺点

优点:

  1. 无状态:服务端不需要存储会话信息

  2. 跨域友好:适合分布式系统和微服务架构

  3. 自包含:令牌中包含所有必要信息

  4. 扩展性好:可以轻松添加自定义声明

缺点:

  1. 无法主动失效:一旦签发,在到期前一直有效

  2. 令牌大小:包含的信息越多,令牌越大

  3. 安全性依赖:完全依赖签名,密钥泄露后果严重

三、Token+Redis方案

3.1 Token+Redis是什么?

Token+Redis方案使用随机生成的令牌作为用户会话的标识,将会话数据存储在Redis中。

这种方案本质上是有状态的,服务端需要维护会话状态。

3.2 Token+Redis的工作流程

3.3 Token+Redis的Java实现示例

import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import java.util.UUID;
import java.util.concurrent.TimeUnit;@Component
public class RedisSessionManager {private final RedisTemplate<String, Object> redisTemplate;// 会话过期时间:2小时private static final long SESSION_EXPIRE_TIME = 2 * 60 * 60;public RedisSessionManager(RedisTemplate<String, Object> redisTemplate) {this.redisTemplate = redisTemplate;}/*** 创建会话*/public String createSession(User user) {String token = generateToken();SessionInfo sessionInfo = new SessionInfo(user.getId(), user.getUsername(), user.getRoles());redisTemplate.opsForValue().set(getRedisKey(token), sessionInfo, SESSION_EXPIRE_TIME, TimeUnit.SECONDS);return token;}/*** 获取会话信息*/public SessionInfo getSession(String token) {return (SessionInfo) redisTemplate.opsForValue().get(getRedisKey(token));}/*** 删除会话*/public void deleteSession(String token) {redisTemplate.delete(getRedisKey(token));}/*** 刷新会话有效期*/public void refreshSession(String token) {redisTemplate.expire(getRedisKey(token), SESSION_EXPIRE_TIME, TimeUnit.SECONDS);}/*** 生成随机token*/private String generateToken() {return UUID.randomUUID().toString().replace("-", "");}/*** 获取Redis key*/private String getRedisKey(String token) {return "session:" + token;}/*** 会话信息类*/@Data@AllArgsConstructorpublic static class SessionInfo {private String userId;private String username;private List<String> roles;private long createTime;public SessionInfo(String userId, String username, List<String> roles) {this.userId = userId;this.username = username;this.roles = roles;this.createTime = System.currentTimeMillis();}}
}

3.4 Token+Redis的优点和缺点

优点:

  1. 主动控制:可以随时使特定令牌失效

  2. 信息量小:令牌只是一个标识符,不会太大

  3. 灵活性高:可以存储复杂的会话状态

  4. 安全性好:令牌泄露可以立即撤销

缺点:

  1. 有状态:服务端需要存储会话信息

  2. Redis依赖:Redis成为单点故障源

  3. 网络开销:每次请求都需要查询Redis

  4. 扩展性挑战:需要处理Redis集群和数据同步

四、深度对比分析

4.1 性能对比

从性能角度,两种方案有显著差异:

方面

JWT

Token+Redis

认证速度

快(本地验证)

慢(需要Redis查询)

网络开销

大(每次请求都需要访问Redis)

服务端压力

大(Redis需要处理大量查询)

扩展成本

高(需要维护Redis集群)

4.2 安全性对比

安全性是认证方案的核心考量因素:

JWT安全性考虑:

  1. 密钥管理:签名密钥需要严格保护,定期轮换

  2. 令牌泄露:无法主动失效,只能等待自动过期

  3. 算法选择:需要选择安全的签名算法(如HS256、RS256)

Token+Redis安全性考虑:

  1. Redis安全:需要保证Redis实例的安全性

  2. 令牌随机性:令牌必须足够随机,防止猜测

  3. 传输安全:需要HTTPS防止令牌被窃听

4.3 适用场景对比

不同的业务场景适合不同的方案:

适合JWT的场景:

  1. 分布式系统和微服务架构

  2. 需要跨域认证的单页应用(SPA)

  3. 无状态API服务

  4. 移动应用后端

适合Token+Redis的场景:

  1. 需要精细控制会话的企业应用

  2. 需要实时吊销权限的系统

  3. 会话信息复杂的传统Web应用

  4. 对安全性要求极高的金融系统

五、混合方案

有些小伙伴在工作中可能会想:能不能结合两种方案的优点?

答案是肯定的!

下面介绍一种混合方案:

5.1 短期JWT + Redis黑名单

这种方案使用短期有效的JWT,配合Redis黑名单实现主动注销:

public class HybridAuthManager {private final JwtUtil jwtUtil;private final RedisTemplate<String, Object> redisTemplate;// JWT短期有效期:15分钟private static final long SHORT_EXPIRATION = 15 * 60 * 1000;// 刷新令牌有效期:7天private static final long REFRESH_EXPIRATION = 7 * 24 * 60 * 60 * 1000;/*** 生成访问令牌和刷新令牌*/public AuthResponse generateTokenPair(User user) {// 生成短期访问令牌String accessToken = jwtUtil.generateToken(user.getId(), user.getUsername(), user.getRoles(), SHORT_EXPIRATION);// 生成长期刷新令牌String refreshToken = UUID.randomUUID().toString();// 存储刷新令牌到RedisstoreRefreshToken(refreshToken, user.getId());returnnew AuthResponse(accessToken, refreshToken);}/*** 刷新访问令牌*/public String refreshAccessToken(String refreshToken) {// 验证刷新令牌有效性String userId = validateRefreshToken(refreshToken);if (userId == null) {thrownew RuntimeException("刷新令牌无效");}// 获取用户信息User user = userService.getUserById(userId);// 生成新的访问令牌return jwtUtil.generateToken(user.getId(), user.getUsername(), user.getRoles(), SHORT_EXPIRATION);}/*** 注销令牌*/public void logout(String accessToken, String refreshToken) {// 将访问令牌加入黑名单(剩余有效期内)Claims claims = jwtUtil.parseToken(accessToken);long expiration = claims.getExpiration().getTime() - System.currentTimeMillis();if (expiration > 0) {redisTemplate.opsForValue().set("blacklist:" + accessToken, "logout", expiration, TimeUnit.MILLISECONDS);}// 删除刷新令牌if (refreshToken != null) {redisTemplate.delete("refresh_token:" + refreshToken);}}/*** 验证令牌是否在黑名单中*/public boolean isTokenBlacklisted(String token) {return redisTemplate.hasKey("blacklist:" + token);}
}

5.2 混合方案工作流程

六、实际项目选型建议

根据我多年的工作经验,给大家一些实用的选型建议:

6.1 选择JWT当以下情况成立时:

  1. 系统是分布式架构,需要无状态认证。

  2. 需要支持跨域认证(如多个前端应用共享后端)。

  3. API消费者主要是第三方应用或移动端。

  4. 团队有能力管理好密钥和令牌安全。

6.2 选择Token+Redis当以下情况成立时:

  1. 系统是单体或少量服务的架构。

  2. 需要精细的会话控制和实时权限管理。

  3. 有专业的运维团队维护Redis集群。

  4. 对安全性要求极高,需要即时吊销能力。

6.3 选择混合方案当以下情况成立时:

  1. 既需要JWT的无状态特性,又需要主动注销能力。

  2. 系统对用户体验要求高(避免频繁登录)。

  3. 有能力处理稍复杂的令牌管理逻辑。

  4. 需要平衡安全性和便利性。

总结

通过上面的详细分析,JWT和token+redis这两种方案,各有优缺点和适用场景。

我们可以得出以下结论:

  1. 没有绝对的最好方案:只有最适合具体业务场景的方案。

  2. JWT优势在无状态和扩展性:适合分布式系统和API优先的架构。

  3. Token+Redis优势在控制和灵活性:适合需要精细会话管理的企业应用。

  4. 混合方案取长补短:适合大多数现代Web应用。

有些小伙伴在工作中可能会盲目追求技术的新颖性,或者过度设计认证方案。

我的建议是:从实际业务需求出发,选择最简单可靠的方案

对于大多数应用来说,我推荐采用混合方案:

  • 使用短期JWT保证API的无状态特性。

  • 使用刷新令牌机制优化用户体验。

  • 使用Redis黑名单提供主动注销能力。

  • 使用HTTPS和严格的密钥管理保证安全性。

无论选择哪种方案,都要记住:安全不是一个功能,而是一个过程。

希望这篇文章能帮助大家在技术选型时做出更明智的决策。

http://www.dtcms.com/a/395309.html

相关文章:

  • 基于react的前端项目开发和实战总结(umi框架)
  • 【iOS】YYModel
  • 修改docker配置使其支持本机tcp连接
  • ReportFragment:Android 生命周期的桥梁与兼容性解决方案
  • 力扣Hot100--234.回文链表
  • 视觉语言大模型(VLM)的产业落地:从Qwen-VL技术解析到医疗、车险行业革新
  • 零基础新手小白快速了解掌握服务集群与自动化运维(七)Nginx模块--Nginx Web服务
  • 一个硬盘选MBR 还是GPT
  • 【含文档+PPT+源码】基于GPT+SpringBoot的个人健康管理与咨询系统设计与实现
  • 【项目实战 Day5】springboot + vue 苍穹外卖系统(Redis + 店铺经营状态模块 完结)
  • 旧衣回收小程序:非技术视角下的价值重构与发展前景
  • 使用vue-i18n实现语言切换
  • 做小程序找哪家公司,解析小程序开发定制公司哪家适合你
  • 【python】python进阶——math模块
  • NHD-6108 全自动远、近光检测仪:智能高效的汽车灯光检测方案
  • 《 Linux 点滴漫谈: 一 》开源之路:Linux 的历史、演进与未来趋势
  • C#和微软System.Speech.Synthesis库实现语音合成
  • C++概述 (一)
  • 【开题答辩全过程】以 基于springboot的高校仪器共享管理系统设计和实现为例,包含答辩的问题和答案
  • 【python】FastAPI简介
  • IDEA lombok注解无效的问题,运行时提示java: 找不到符号或者方法
  • Windows 系统部署 Kronos 金融 K 线基础模型——基于 EPGF 架构
  • 010 Rust流程控制
  • MyBatisPlus快速入门:简化CRUD操作
  • 网络编程套接字(三)---简单的TCP网络程序
  • 背景建模(基于视频,超炫)项目实战!
  • ios26版本回退到ios18
  • OpenCV直方图比较:原理与四种方法详解
  • OpenCV - 图像金字塔
  • 寄存柜频繁维护还卡顿?杰和IB2-281主板:智能化升级高效省心