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 的实现

相关推荐
侠客行031713 小时前
Mybatis连接池实现及池化模式
java·mybatis·源码阅读
蛇皮划水怪13 小时前
深入浅出LangChain4J
java·langchain·llm
老毛肚15 小时前
MyBatis体系结构与工作原理 上篇
java·mybatis
风流倜傥唐伯虎15 小时前
Spring Boot Jar包生产级启停脚本
java·运维·spring boot
Yvonne爱编码16 小时前
JAVA数据结构 DAY6-栈和队列
java·开发语言·数据结构·python
Re.不晚16 小时前
JAVA进阶之路——无奖问答挑战1
java·开发语言
你这个代码我看不懂16 小时前
@ConditionalOnProperty不直接使用松绑定规则
java·开发语言
fuquxiaoguang16 小时前
深入浅出:使用MDC构建SpringBoot全链路请求追踪系统
java·spring boot·后端·调用链分析
琹箐16 小时前
最大堆和最小堆 实现思路
java·开发语言·算法
__WanG16 小时前
JavaTuples 库分析
java