通过Redis实现Token黑名单机制的具体方案

 更新时间:2025年11月06日 08:57:18   作者:梁云亮  
如果没有为Token提供主动失效机制,一旦Token被签发,在过期之前将一直有效,存一些安全隐患,所以本文通过将已失效的Token存储在Redis中,可以确保Token在被主动注销后无法继续使用,需要的朋友可以参考下

1. 概述

如果没有为Token提供主动失效机制。一旦Token被签发,在过期之前将一直有效,存在以下安全隐患:

  1. 无法主动注销:用户注销后,已签发的Token仍然有效
  2. 安全风险:Token泄露后无法立即失效
  3. 缺乏控制:无法对特定Token进行精准控制

本博客通过将已失效的Token存储在Redis中,可以确保Token在被主动注销后无法继续使用,提高了系统的安全性和可控性。

2. 具体方案

2.1 设计思路

通过在Redis中维护一个Token黑名单来实现Token的主动失效功能:

  1. 当用户注销或Token需要主动失效时,将Token加入黑名单
  2. 在验证Token时,先检查是否在黑名单中
  3. 如果在黑名单中,则认为Token已失效

2.2 Redis存储设计

Key: token:blacklist:{token}
Value: "blacklisted"
Expire: Token剩余有效时间

2.3 TokenUtil中新增方法

2.3.1 addToBlacklist方法

/**
 * 将Token加入黑名单,实现主动失效
 *
 * @param token JWT token
 * @return 是否成功加入黑名单
 */
public boolean addToBlacklist(String token) {
    try {
        // 获取Token的过期时间
        Date expirationDate = getExpirationDateFromToken(token);
        if (expirationDate == null) {
            return false;
        }

        // 计算剩余有效时间
        long remainingTime = expirationDate.getTime() - System.currentTimeMillis();
        if (remainingTime <= 0) {
            return false;
        }

        // 将Token加入Redis黑名单,设置过期时间为Token剩余有效时间
        String key = TOKEN_BLACKLIST_PREFIX + token;
        return redisUtil.set(key, "blacklisted", remainingTime / 1000);
    } catch (Exception e) {
        return false;
    }
}

2.3.2 isTokenInBlacklist方法

/**
 * 检查Token是否在黑名单中
 *
 * @param token JWT token
 * @return 是否在黑名单中
 */
public boolean isTokenInBlacklist(String token) {
    String key = TOKEN_BLACKLIST_PREFIX + token;
    return redisUtil.exists(key);
}

2.3.3 removeFromBlacklist方法

/**
 * 从黑名单中移除Token
 *
 * @param token JWT token
 * @return 是否成功移除
 */
public boolean removeFromBlacklist(String token) {
    String key = TOKEN_BLACKLIST_PREFIX + token;
    return redisUtil.del(key) > 0;
}

2.3.4 修改validateToken方法

/**
 * 验证Token是否合法且未过期
 *
 * @param token JWT token
 * * @return 是否有效
 */
public Boolean validateToken(String token) {
    try {
        // 检查Token是否在黑名单中
        if (isTokenInBlacklist(token)) {
            return false;
        }

        JwtParser parser = Jwts.parser().verifyWith(secretKey).build();
        parser.parseSignedClaims(token);
        return true;
    } catch (JwtException e) {
        System.out.println("JWT exception: " + e.getMessage());
    } catch (IllegalArgumentException e) {
        System.out.println("JWT claims string is empty: " + e.getMessage());
    }
    return false;
}

3. 使用示例

3.1 用户注销时将Token加入黑名单

@PostMapping("/logout")
public ResultBean<String> logout(@RequestHeader("Authorization") String token) {
    if (token != null && token.startsWith("Bearer ")) {
        token = token.substring(7);
      
        // 将Token加入黑名单
        boolean added = tokenUtil.addToBlacklist(token);
        if (added) {
            return ResultBean.success("注销成功");
        }
    }
    return ResultBean.error("注销失败");
}

3.2 在拦截器中验证Token

@Component
public class TokenInterceptor implements HandlerInterceptor {
  
    @Autowired
    private TokenUtil tokenUtil;
  
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String token = request.getHeader("Authorization");
      
        if (token != null && token.startsWith("Bearer ")) {
            token = token.substring(7);
          
            // 验证Token(会自动检查黑名单)
            if (!tokenUtil.validateToken(token)) {
                response.setStatus(HttpStatus.UNAUTHORIZED.value());
                return false;
            }
        }
      
        return true;
    }
}

3.3 管理员强制用户下线

@PostMapping("/admin/forceLogout")
public ResultBean<String> forceLogout(@RequestParam String token) {
    // 将Token加入黑名单
    boolean added = tokenUtil.addToBlacklist(token);
    if (added) {
        return ResultBean.success("用户已强制下线");
    }
    return ResultBean.error("操作失败");
}

4. 优化效果

4.1 安全性提升

  • 实现了Token的主动失效功能
  • 可以精准控制特定Token的可用性
  • 有效防止Token泄露后的安全风险

4.2 控制性增强

  • 提供了细粒度的Token控制能力
  • 支持用户主动注销和管理员强制下线
  • 可以根据业务需求灵活控制Token生命周期

4.3 用户体验改善

  • 用户注销后Token立即失效
  • 管理员可以强制用户下线
  • 提高了系统的安全性和可控性

5. 注意事项

  1. Redis性能: 黑名单存储在Redis中,需要注意Redis的性能和容量
  2. 过期时间: 黑名单中的Token会自动过期,过期时间与Token剩余有效时间一致
  3. 异常处理: 需要妥善处理Redis操作可能出现的异常
  4. 并发控制: 在高并发场景下需要注意Redis操作的并发控制

以上就是通过Redis实现Token黑名单机制的具体方案的详细内容,更多关于Redis实现Token黑名单机制的资料请关注脚本之家其它相关文章!

相关文章

  • redis和rabbitmq实现延时队列的示例代码

    redis和rabbitmq实现延时队列的示例代码

    在高并发场景下,延迟队列显得尤为重要,本文主要介绍了两种方式,redis和rabbitmq实现延时队列,具有一定的参考价值,感兴趣的可以了解一下
    2024-03-03
  • Redis底层数据结构之dict、ziplist、quicklist详解

    Redis底层数据结构之dict、ziplist、quicklist详解

    本文给大家详细介绍了Redis的底层数据结构:dict、ziplist、quicklist的相关知识,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧
    2021-09-09
  • redis lua脚本实战秒杀和减库存的实现

    redis lua脚本实战秒杀和减库存的实现

    本文主要是学习一下redis lua脚本的编写,以及在redisson这个redis客户端中是怎样使用的,实战一下秒杀场景redis减库存lua脚本的编写,并伪真实环境压测查看效果。感兴趣的可以了解一下
    2021-11-11
  • Redis集群部署模式的不同实现过程

    Redis集群部署模式的不同实现过程

    这篇文章主要介绍了Redis集群部署模式的不同实现过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2025-05-05
  • 如何打造redis缓存组件

    如何打造redis缓存组件

    文章介绍了如何使用热插拔AOP、反射、Redis自定义注解和SpringEL表达式来打造一个优雅的Redis缓存组件,通过这种方式,可以重构和简化缓存代码,并提供了Redis配置和自定义注解的详细说明,文章还包含了AOP测试的总结,并鼓励读者参考和支持
    2024-12-12
  • Redis中的延迟双删

    Redis中的延迟双删

    这篇文章主要介绍了Redis中的延迟双删问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-04-04
  • 你真的了解redis为什么要提供pipeline功能

    你真的了解redis为什么要提供pipeline功能

    Redis本身是一个cs模式的tcp server, client可以通过一个socket连续发起多个请求命令。这篇文章带领大家学习redis为什么要提供pipeline功能,需要的朋友可以参考下
    2021-06-06
  • 浅谈redis的maxmemory设置以及淘汰策略

    浅谈redis的maxmemory设置以及淘汰策略

    下面小编就为大家带来一篇浅谈redis的maxmemory设置以及淘汰策略。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-03-03
  • 压缩Redis里的字符串大对象操作

    压缩Redis里的字符串大对象操作

    这篇文章主要介绍了压缩Redis里的字符串大对象操作,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-06-06
  • Redis中Scan命令的基本使用教程

    Redis中Scan命令的基本使用教程

    这篇文章主要给大家介绍了关于Redis中Scan命令的基本使用教程,文中通过示例代码介绍的非常详细,对大家学习或者使用Redis具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2019-06-06

最新评论