基于Redis实现双加密Token的示例代码

 更新时间:2025年01月13日 11:38:39   作者:Asthenia0412  
在现代分布式系统中,Token管理是身份验证和授权的核心部分,本文将深入分析一个基于Redis的Token管理实现,探讨其设计思路、关键代码逻辑以及实现细节,通过对源码的逐层剖析,帮助读者更好地理解Token管理的实现原理,需要的朋友可以参考下

1. 核心设计思路

TokenStore类的核心设计目标是实现一个高效、安全的Token管理系统,主要功能包括:

  • Token的生成与存储:用户登录时生成accessTokenrefreshToken,并将其存储在Redis中。
  • Token的刷新:通过refreshToken获取新的accessToken
  • Token的删除:删除用户的所有Token,通常用于用户注销或管理员禁用用户。
  • 用户信息的获取:通过accessToken获取用户的详细信息。

为了实现这些功能,TokenStore类依赖Redis作为存储介质,利用Redis的高性能和丰富的数据结构(如StringSet)来管理Token和用户信息。

2. 关键代码逻辑分析

2.1 Token的生成与存储

在用户登录时,系统会生成一个accessToken和一个refreshToken,并将它们存储在Redis中。以下是storeAccessToken方法的核心逻辑:

public TokenInfoBO storeAccessToken(UserInfoInTokenBO userInfoInToken) {
    TokenInfoBO tokenInfoBO = new TokenInfoBO();
    String accessToken = IdUtil.simpleUUID(); // 生成随机的accessToken
    String refreshToken = IdUtil.simpleUUID(); // 生成随机的refreshToken

    tokenInfoBO.setUserInfoInToken(userInfoInToken);
    tokenInfoBO.setExpiresIn(getExpiresIn(userInfoInToken.getSysType())); // 设置Token过期时间

    String uidToAccessKeyStr = getUidToAccessKey(getApprovalKey(userInfoInToken)); // 生成uid_to_access的Redis Key
    String accessKeyStr = getAccessKey(accessToken); // 生成access_token的Redis Key
    String refreshToAccessKeyStr = getRefreshToAccessKey(refreshToken); // 生成refresh_to_access的Redis Key

    // 将新的Token加入现有Token列表
    List<String> existsAccessTokens = new ArrayList<>();
    existsAccessTokens.add(accessToken + StrUtil.COLON + refreshToken);

    // 检查并清理过期的Token
    Long size = redisTemplate.opsForSet().size(uidToAccessKeyStr);
    if (size != null && size != 0) {
        List<String> tokenInfoBoList = stringRedisTemplate.opsForSet().pop(uidToAccessKeyStr, size);
        if (tokenInfoBoList != null) {
            for (String accessTokenWithRefreshToken : tokenInfoBoList) {
                String[] accessTokenWithRefreshTokenArr = accessTokenWithRefreshToken.split(StrUtil.COLON);
                String accessTokenData = accessTokenWithRefreshTokenArr[0];
                if (BooleanUtil.isTrue(stringRedisTemplate.hasKey(getAccessKey(accessTokenData)))) {
                    existsAccessTokens.add(accessTokenWithRefreshToken);
                }
            }
        }
    }

    // 使用Redis管道批量操作
    redisTemplate.executePipelined((RedisCallback<Object>) connection -> {
        long expiresIn = tokenInfoBO.getExpiresIn();

        byte[] uidKey = uidToAccessKeyStr.getBytes(StandardCharsets.UTF_8);
        byte[] refreshKey = refreshToAccessKeyStr.getBytes(StandardCharsets.UTF_8);
        byte[] accessKey = accessKeyStr.getBytes(StandardCharsets.UTF_8);

        // 将Token列表存入Redis
        for (String existsAccessToken : existsAccessTokens) {
            connection.sAdd(uidKey, existsAccessToken.getBytes(StandardCharsets.UTF_8));
        }

        // 设置uid_to_access的过期时间
        connection.expire(uidKey, expiresIn);

        // 存储refresh_to_access的映射
        connection.setEx(refreshKey, expiresIn, accessToken.getBytes(StandardCharsets.UTF_8));

        // 存储access_token对应的用户信息
        connection.setEx(accessKey, expiresIn, Objects.requireNonNull(redisSerializer.serialize(userInfoInToken)));

        return null;
    });

    // 返回加密后的Token
    tokenInfoBO.setAccessToken(encryptToken(accessToken, userInfoInToken.getSysType()));
    tokenInfoBO.setRefreshToken(encryptToken(refreshToken, userInfoInToken.getSysType()));

    return tokenInfoBO;
}

关键点分析:

  • Token生成:使用IdUtil.simpleUUID()生成随机的accessTokenrefreshToken,确保Token的唯一性。
  • Redis数据结构
    • uid_to_access:使用Set数据结构存储用户的所有Token,方便管理和清理。
    • refresh_to_access:使用String数据结构存储refreshTokenaccessToken的映射。
    • access_token:使用String数据结构存储accessToken对应的用户信息。
  • 过期时间管理:根据用户类型(sysType)设置不同的Token过期时间,普通用户和管理员的Token过期时间不同。
  • Redis管道操作:通过executePipelined方法批量执行Redis操作,减少网络开销,提升性能。

2.2 Token的刷新

accessToken过期时,用户可以通过refreshToken获取新的accessToken。以下是refreshToken方法的核心逻辑:

public ServerResponseEntity<TokenInfoBO> refreshToken(String refreshToken) {
    if (StrUtil.isBlank(refreshToken)) {
        return ServerResponseEntity.showFailMsg("refreshToken is blank");
    }
    ServerResponseEntity<String> decryptTokenEntity = decryptToken(refreshToken); // 解密refreshToken
    if (!decryptTokenEntity.isSuccess()) {
        return ServerResponseEntity.transform(decryptTokenEntity);
    }
    String realRefreshToken = decryptTokenEntity.getData();
    String accessToken = stringRedisTemplate.opsForValue().get(getRefreshToAccessKey(realRefreshToken)); // 获取对应的accessToken

    if (StrUtil.isBlank(accessToken)) {
        return ServerResponseEntity.showFailMsg("refreshToken 已过期");
    }
    ServerResponseEntity<UserInfoInTokenBO> userInfoByAccessTokenEntity = getUserInfoByAccessToken(accessToken, false);

    if (!userInfoByAccessTokenEntity.isSuccess()) {
        return ServerResponseEntity.showFailMsg("refreshToken 已过期");
    }

    UserInfoInTokenBO userInfoInTokenBO = userInfoByAccessTokenEntity.getData();
    // 删除旧的refresh_token和access_token
    stringRedisTemplate.delete(getRefreshToAccessKey(realRefreshToken));
    stringRedisTemplate.delete(getAccessKey(accessToken));
    // 生成新的Token
    TokenInfoBO tokenInfoBO = storeAccessToken(userInfoInTokenBO);

    return ServerResponseEntity.success(tokenInfoBO);
}

关键点分析:

  • Token解密:通过decryptToken方法解密refreshToken,确保Token的安全性。
  • Token映射:通过refresh_to_access的映射关系,找到对应的accessToken
  • Token清理:删除旧的refreshTokenaccessToken,防止Token被重复使用。
  • 新Token生成:调用storeAccessToken方法生成新的Token。

2.3 Token的删除

在某些情况下(如用户注销或管理员禁用用户),需要删除用户的所有Token。以下是deleteAllToken方法的核心逻辑:

public void deleteAllToken(String appId, Long uid) {
    String uidKey = getUidToAccessKey(getApprovalKey(appId, uid)); // 生成uid_to_access的Redis Key
    Long size = redisTemplate.opsForSet().size(uidKey);
    if (size == null || size == 0) {
        return;
    }
    List<String> tokenInfoBoList = stringRedisTemplate.opsForSet().pop(uidKey, size);

    if (CollUtil.isEmpty(tokenInfoBoList)) {
        return;
    }

    // 遍历并删除所有Token
    for (String accessTokenWithRefreshToken : tokenInfoBoList) {
        String[] accessTokenWithRefreshTokenArr = accessTokenWithRefreshToken.split(StrUtil.COLON);
        String accessToken = accessTokenWithRefreshTokenArr[0];
        String refreshToken = accessTokenWithRefreshTokenArr[1];
        redisTemplate.delete(getRefreshToAccessKey(refreshToken));
        redisTemplate.delete(getAccessKey(accessToken));
    }
    redisTemplate.delete(uidKey); // 删除uid_to_access的Key
}

关键点分析:

  • 批量删除:通过uid_to_accessSet数据结构,获取用户的所有Token并批量删除。
  • 清理映射关系:删除refresh_to_accessaccess_token的映射关系,确保Token完全失效。

3. 总结

通过对TokenStore类的深入分析,我们可以看到其设计思路清晰,代码逻辑严谨。通过Redis的高效存储和丰富的数据结构,实现了Token的生成、刷新、删除以及用户信息的获取。这种设计不仅保证了系统的安全性,还提升了系统的性能和可扩展性。

希望本文的源码分析能够帮助你更好地理解Token管理的实现原理。

到此这篇关于基于Redis实现双加密Token的示例代码的文章就介绍到这了,更多相关Redis双加密Token内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 详解如何清理Redis内存碎片

    详解如何清理Redis内存碎片

    操作系统的剩余空间总量足够,但申请一块N字节连续地址的空间时,剩余内存空间中没有大小为N字节的连续空间,那么这些剩余内存空间中,小于N字节的连续内存空间就是内存碎片,本文详细介绍了如何清理Redis内存碎片,需要的朋友可以参考一下
    2023-04-04
  • Redis 存取 JSON 数据示例操作

    Redis 存取 JSON 数据示例操作

    JSON 是我们常用的数据类型,当我们需要在Redis中保存json数据时是怎么存放的呢?一般是用String或者Hash,但还是不太方便,无法灵活的操作json 数据,下面通过本文给大家介绍Redis存取JSON 数据示例操作,感兴趣的的朋友一起看看吧
    2024-02-02
  • Redis+自定义注解+AOP实现声明式注解缓存查询的示例

    Redis+自定义注解+AOP实现声明式注解缓存查询的示例

    实际项目中,会遇到很多查询数据的场景,这些数据更新频率也不是很高,一般我们在业务处理时,会对这些数据进行缓存,本文主要介绍了Redis+自定义注解+AOP实现声明式注解缓存查询的示例,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来一起学习学习吧
    2025-04-04
  • 还不懂Redis?看完这个趣味小故事就明白了!

    还不懂Redis?看完这个趣味小故事就明白了!

    这篇文章主要用趣味性的方法讲解了redis是什么?并且和MYSQL的区别是什么,有对redis不太懂的小伙伴可以来看一下吧
    2020-12-12
  • 分布式锁三种实现方式及对比

    分布式锁三种实现方式及对比

    这篇文章主要介绍了分布式锁三种实现方式及对比,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-04-04
  • Redis禁用命令、危险命令及规避方法

    Redis禁用命令、危险命令及规避方法

    这篇文章主要介绍了Redis禁用命令、危险命令及规避方法,本文介绍了个非常致命的两个命令以及用配置文件禁用这些命令的方法,需要的朋友可以参考下
    2015-06-06
  • 利用Redis如何实现自动补全功能

    利用Redis如何实现自动补全功能

    这篇文章主要给大家介绍了关于如何利用Redis如何实现自动补全功能的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用Redis具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2019-09-09
  • redis源码分析教程之压缩链表ziplist详解

    redis源码分析教程之压缩链表ziplist详解

    ziplist结构在redis运用非常广泛,是列表、字典等数据类型的底层结构之一。ziplist的优点在于能够一定程度地节约内存。下面这篇文章主要给大家介绍了关于redis源码分析教程之压缩链表ziplist的相关资料,需要的朋友可以参考借鉴,下面来一起看看吧。
    2017-12-12
  • Redis慢查询日志与监视器问题

    Redis慢查询日志与监视器问题

    这篇文章主要介绍了Redis慢查询日志与监视器问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-12-12
  • redis客户端实现高可用读写分离的方式详解

    redis客户端实现高可用读写分离的方式详解

    基于sentienl 获取和动态感知 master、slaves节点信息的变化,我们的读写分离客户端就能具备高可用+动态扩容感知能力了,接下来通过本文给大家分享redis客户端实现高可用读写分离的方式,感兴趣的朋友一起看看吧
    2021-07-07

最新评论