接口调用出问题,一半出在鉴权。Token 怎么拿、缓存在哪、过期怎么刷、多账号怎么隔离,这几件事没理顺,后面每个接口调用都要被 401 折腾一遍。这篇把鉴权这一段单独讲透,再接上正常调用。
一、拿 Token:appId 换 Token 的两步
第一步用 appId 和 secret 换 Token,第二步拿 Token 调业务接口。Token 不是永久有效的,返回里一般带一个有效期字段,写法见Eyun 开发文档。
要点是别每次调用都去换 Token——换 Token 本身也是接口调用,高频换会触发限制。拿到就缓存,用到快过期再换。
二、缓存与刷新:别等到 401 才反应
缓存 Token 要连过期时间一起存。推荐在过期前的一个窗口主动刷新,而不是等接口返回 401 再处理——401 意味着这次调用已经失败了,用户那一侧已经受影响。
并发场景要注意:多个线程同时发现 Token 快过期,会一起去刷新。用锁或单飞(single-flight)保证只有一个在刷,其他等结果。
三、多账号隔离:一套配置一个命名空间
多账号时不能共用一个 Token。每个账号有自己的 appId 和 Token,缓存也要按账号分开存,key 用账号 ID 做前缀,取的时候按账号取。
这一段踩过坑:早期共用一个缓存 key,结果 A 账号的 Token 被 B 账号覆盖,两边调用全挂。
鉴权环节对照
环节 | 做法 | 要点 | 常见坑 |
|---|---|---|---|
获取 | appId+secret 换 Token | 不重复换 | 每次调用都换 |
缓存 | 带过期时间存储 | 提前主动刷新 | 等 401 才反应 |
并发 | 单飞保证唯一刷新 | 其他等待结果 | 并发重复刷新 |
多账号 | 按账号分命名空间 | 独立 Token | 缓存 key 撞车 |
鉴权与调用实现
import threading import time class TokenVault: def __init__(self, api, app_id, secret): self.api = api self.app_id = app_id self.secret = secret self.tokens = {} # account -> (token, expire_at) self.locks = {} def token_of(self, account): token, expire = self.tokens.get(account, (None, 0)) if token and time.time() < expire - 60: # 提前60秒才算可用 return token lock = self.locks.setdefault(account, threading.Lock()) with lock: token, expire = self.tokens.get(account, (None, 0)) if token and time.time() < expire - 60: return token # 双检,别人已刷好 resp = self.api.post("/auth/token", { "app_id": self.app_id, "secret": self.secret, "account": account, }) token = resp["token"] expire = time.time() + resp["expires_in"] self.tokens[account] = (token, expire) # 按账号分开存 return token def call(self, account, path, payload): # 统一出口:自动带 Token,401 自动刷新重试一次 for _ in range(2): token = self.token_of(account) r = self.api.post(path, payload, headers={"Authorization": token}) if r.status_code == 401: self.tokens.pop(account, None) # 清掉旧 Token continue return r.json() return {"code": -1, "msg": "鉴权失败"}落地建议
Token 拿到就缓存,别每次调用都换。过期前主动刷新,别等 401 影响用户请求。并发刷新用单飞,避免多线程重复换。多账号按账号分命名空间存 Token,防止缓存互相覆盖。所有调用走统一出口,401 自动重试一次。微信侧的鉴权与业务接口由 Eyun 这类个人微信API平台 提供,Token 管理与调用封装在自建服务实现,具体字段以Eyun 平台为准。