首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

微信支付 Java 開發:Access Token 獲取、緩存與定時刷新最佳實踐

     編輯:Java 相關知識

Access Token 核心機制

  • 全局唯一憑證:公眾號/商戶號調用所有微信接口的憑證。
  • 有效期 7200 秒(2 小時):過期失效,需主動刷新。
  • 長度 ≤ 512 字節:存儲空間需預留充足。
  • 並發安全要求:多業務服務器共用一個 Token,**嚴禁各自刷新**,否則導致 Token 相互覆蓋、業務失效。

架構設計:中控服務器模式

+----------------+      1. 定時刷新/主動刷新       +------------------+
|  微信服務器     | ◄────────────────────────────── |  中控服務器       |
|  (Token簽發)   | ─────────────────────────────► |  (單例 Token 管理) |
+----------------+      2. 返回 access_token       +------------------+
                                                        │
                            3. 業務服務器獲取 Token      │
                              (Redis/本地緩存/HTTP API)  ▼
+----------------+      4. 攜帶 Token 調用業務接口       +------------------+
|  微信服務器     | ◄────────────────────────────── |  業務服務器 A/B/C  |
|  (業務接口)     | ─────────────────────────────► |  (統一從中控獲取)   |
+----------------+      5. 返回業務數據              +------------------+

核心代碼實現(Spring Boot + Redis)

1. 配置屬性

wechat:
  appid: wx8888888888888888
  appsecret: your_app_secret
  token:
    refresh-cron: "0 0/10 * * * ?"  # 每 10 分鐘刷新,留足 1 小時緩衝
    redis-key: "wechat:access_token"

2. Token 服務接口

public interface AccessTokenService {
    String getAccessToken();           // 獲取有效 Token(自動刷新)
    void refreshAccessToken();         // 手動觸發刷新
    boolean isTokenValid(String token); // 校驗 Token 有效性
}

3. 定時任務實現

@Component
@RequiredArgsConstructor
@Slf4j
public class WechatTokenRefresher {

    private final WechatProperties props;
    private final RestTemplate restTemplate;
    private final StringRedisTemplate redisTemplate;

    @Scheduled(cron = "${wechat.token.refresh-cron}")
    public void scheduledRefresh() {
        refreshAccessToken();
    }

    @Transactional
    public void refreshAccessToken() {
        String url = "https://api.weixin.qq.com/cgi-bin/token"
                + "?grant_type=client_credential"
                + "&appid=" + props.getAppid()
                + "&secret=" + props.getAppsecret();

        try {
            Map<String, Object> resp = restTemplate.getForObject(url, Map.class);
            if (resp == null) throw new IllegalStateException("微信響應為空");

            if (resp.containsKey("errcode")) {
                log.error("獲取 Token 失敗: {}", resp);
                throw new WechatApiException((Integer) resp.get("errcode"), (String) resp.get("errmsg"));
            }

            String token = (String) resp.get("access_token");
            int expiresIn = ((Number) resp.get("expires_in")).intValue(); // 7200

            // Redis 存儲,過期時間比微信返回提前 5 分鐘,保證緩存內 Token 必然有效
            redisTemplate.opsForValue().set(
                props.getToken().getRedisKey(),
                token,
                expiresIn - 300, TimeUnit.SECONDS
            );
            log.info("Access Token 刷新成功,有效期 {} 秒", expiresIn);

        } catch (Exception e) {
            log.error("Token 刷新異常", e);
            // 可在此接入告警通知(鉤子/郵件/短信)
        }
    }

    public String getAccessToken() {
        String token = redisTemplate.opsForValue().get(props.getToken().getRedisKey());
        if (StringUtils.isBlank(token)) {
            // 雙重檢查鎖或分布式鎖防止並發刷新
            refreshAccessToken();
            token = redisTemplate.opsForValue().get(props.getToken().getRedisKey());
        }
        return token;
    }
}

4. 業務調用攔截器(自動注入 Token)

@Component
@RequiredArgsConstructor
public class WechatApiInterceptor implements ClientHttpRequestInterceptor {

    private final AccessTokenService tokenService;

    @Override
    public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) 
            throws IOException {
        String token = tokenService.getAccessToken();
        URI uri = request.getURI();
        String newUri = UriComponentsBuilder.fromUri(uri)
                .queryParam("access_token", token)
                .build(true).toUriString();
        request.getHeaders().setContentType(MediaType.APPLICATION_JSON);
        return execution.execute(new HttpRequestWrapper(request, newUri), body);
    }
}

關鍵容錯與優化策略

  1. 雙 Token 平滑過渡:微信保證刷新前後 5 分鐘內新舊 Token 並存。中控服務器刷新成功前,繼續向業務提供舊 Token。
  2. 主動刷新兜底:業務調用返回 40001/42001 (Token 失效/過期) 時,觸發 refreshAccessToken() 並重試一次。
  3. 分布式鎖防並發刷新:集群部署時,使用 Redis Redisson 或 ZooKeeper 實現 lock("wechat:token:refresh"),避免多節點同時刷新導致頻率限制。
  4. 監控告警:刷新失敗、Token 即將過期(< 10 分鐘)、調用失效率異常升高,均需接入 Prometheus + Grafana + Alertmanager 告警。

常見錯誤碼速查

errcodeerrmsg原因處理
40001invalid credentialAppID/AppSecret 錯誤或 Token 過期檢查配置、強制刷新
40013invalid appidAppID 不存在或異常登錄公眾平台核實賬號狀態
42001access_token expiredToken 過期觸發刷新流程
45009api freq out of limit調用頻率超限指數退避重試、申請提額

結語

Access Token 是微信支付/開放平台開發的「通行證」。建立**中控服務器 + Redis 緩存 + 定時刷新 + 失效重試 + 監控告警**的完整體系,是保障業務穩定運行的基石。切記:Token 只有一個來源,業務只負責消費,不負責生產。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved