Token 管理工具

下面是一套 真正通用、可复用、与具体平台完全解耦的 Token 管理工具设计,满足:

  • 不绑定企业微信
  • ✅ 支持 任意第三方(OAuth / 内部系统 / API Token)
  • ✅ 自动缓存
  • ✅ 自动过期刷新
  • ✅ 并发安全
  • ✅ 可 Spring 注入
  • ✅ 你现在的企业微信 / 以后阿里云 / 钉钉 / 自研系统都能用

一、设计目标(一句话)

TokenManager 只负责:缓存 + 过期判断 + 刷新调度至于"怎么获取 Token",交给调用方提供

👉 这就是策略模式 + 缓存池

二、核心抽象(这是关键)

1️⃣ 通用 Token 接口(不是企业微信)

plain 复制代码
public interface AccessToken {

    /** 获取 token 字符串 */
    String getToken();

    /** 是否已过期 */
    boolean isExpired();
}

2️⃣ 通用 Token 刷新器(最重要)

plain 复制代码
@FunctionalInterface
public interface TokenProvider {

    /**
     * 获取新的 token
     * 由调用方实现(HTTP / SDK / DB / 本地算法 都行)
     */
    AccessToken refresh();
}

⚠️ 注意:TokenManager 完全不知道 token 从哪来

三、通用 TokenManager(核心实现)✅

plain 复制代码
@Component
public class TokenManager {

    /** Token 缓存池 */
    private final Map<String, AccessToken> tokenCache = new ConcurrentHashMap<>();

    /** 锁池(避免全局锁) */
    private final Map<String, Object> lockMap = new ConcurrentHashMap<>();

    /**
     * 获取 token(通用)
     *
     * @param key           token 唯一标识(非常重要)
     * @param tokenProvider token 刷新逻辑
     */
    public String getToken(String key, TokenProvider tokenProvider) {
        AccessToken token = tokenCache.get(key);

        if (token == null || token.isExpired()) {
            // 每个 key 一把锁
            Object lock = lockMap.computeIfAbsent(key, k -> new Object());

            synchronized (lock) {
                token = tokenCache.get(key);
                if (token == null || token.isExpired()) {
                    token = tokenProvider.refresh();
                    tokenCache.put(key, token);
                }
            }
        }
        return token.getToken();
    }

    /** 主动清除(可选) */
    public void invalidate(String key) {
        tokenCache.remove(key);
        lockMap.remove(key);
    }
}

📌 这是一个"平台无关"的基础设施类

四、一个通用 Token 实现(可直接用)

plain 复制代码
public class DefaultAccessToken implements AccessToken {

    private final String token;
    private final long expireAt;

    public DefaultAccessToken(String token, long expiresInSeconds) {
        this.token = token;
        // 提前 5 分钟过期
        this.expireAt = System.currentTimeMillis() + (expiresInSeconds - 300) * 1000;
    }

    @Override
    public String getToken() {
        return token;
    }

    @Override
    public boolean isExpired() {
        return System.currentTimeMillis() >= expireAt;
    }
}

五、使用示例(重点:看有多通用)

✅ 1️⃣ 企业微信(你现在的需求)

plain 复制代码
String token = tokenManager.getToken(
    "wx:login",
    () -> {
        WxTokenDTO dto = callWxApi();
        return new DefaultAccessToken(dto.getAccess_token(), dto.getExpires_in());
    }
);

✅ 2️⃣ 钉钉 Token

plain 复制代码
String token = tokenManager.getToken(
    "dingding:token",
    () -> {
        DingTokenDTO dto = dingApi.getToken();
        return new DefaultAccessToken(dto.getAccessToken(), dto.getExpireIn());
    }
);

✅ 3️⃣ 阿里云 RAM Token

plain 复制代码
String token = tokenManager.getToken(
    "aliyun:ram",
    () -> new DefaultAccessToken(fetchAliyunToken(), 3600)
);

✅ 4️⃣ 自研系统 API Token

plain 复制代码
String token = tokenManager.getToken(
    "internal:api",
    () -> new DefaultAccessToken(loginAndGetToken(), 7200)
);

✅ 5️⃣ 本地算法生成 Token(JWT)

plain 复制代码
String token = tokenManager.getToken(
    "jwt:system",
    () -> new DefaultAccessToken(jwtService.generate(), 1800)
);

六、为什么这个方案是"工程级"的?

说明
✅ 完全解耦 TokenManager 不依赖任何平台
✅ 策略模式 TokenProvider 决定"怎么拿 token"
✅ 高并发安全 每个 key 独立锁
✅ 可扩展 新平台不改一行管理代码
✅ 可测试 TokenProvider 可 mock
✅ 可缓存 Map / Redis 都能替换

七、进阶(如果你后面会分布式)

如果你以后是 多节点部署,只需要把:

plain 复制代码
Map<String, AccessToken> tokenCache

换成:

  • Redis
  • Caffeine
  • Guava Cache

👉 TokenManager 设计完全不用变

八、你现在项目中可以这样分层(建议)

plain 复制代码
infrastructure/
 └── token/
     ├── TokenManager.java
     ├── AccessToken.java
     ├── TokenProvider.java
     └── DefaultAccessToken.java

企业微信、钉钉、阿里云都只是 TokenProvider 的实现

相关推荐
Hx_Ma1613 小时前
Map集合的5种遍历方式
java·前端·javascript
小手cool13 小时前
Java 列表中查找最小值和最大值最有效率的方法
java
惊讶的猫13 小时前
多线程同步问题及解决
java·开发语言·jvm
wfsm13 小时前
工厂模式创建动态代理实现类
java·开发语言
好好研究13 小时前
总结SSM设置欢迎页的方式
xml·java·后端·mvc
Hui Baby13 小时前
java -jar 启动原理
java·pycharm·jar
weixin_5112552113 小时前
更新jar内资源和代码
java·jar
木井巳13 小时前
【递归算法】验证二叉搜索树
java·算法·leetcode·深度优先·剪枝
不当菜虚困13 小时前
windows下HSDB导出class文件报错【java.io.IOException : 系统找不到指定的路径。】
java·开发语言
小马爱打代码13 小时前
Spring Boot:第三方 API 调用的企业级容错设计
java·spring boot·后端