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

Java 微信公眾號開發入門:服務器接入驗證與 Access Token 管理機制

     編輯:Java 相關知識

微信公眾號開發第一步:服務器接入

開發者需在公眾平台配置:

  • 服務器地址 (URL):接收微信推送的 HTTP/HTTPS 端點,僅支持 80/443 端口。
  • Token:開發者自定義字符串,用於簽名驗證。
  • EncodingAESKey:消息加解密密鑰(可選,明文模式可不填)。

接入驗證流程 (GET 請求)

微信服務器發送 GET 請求攜帶四個參數:

驗證算法

  1. 將 token、timestamp、nonce 三個參數進行字典序排序。
  2. 將三個參數字符串拼接成一個字符串進行 SHA1 加密。
  3. 開發者獲得加密後的字符串與 signature 對比,相等則驗證通過,原樣返回 echostr。

Servlet 實現


@WebServlet("/wechat")
public class WechatServlet extends HttpServlet {
    private static final String TOKEN = "your_token_here"; // 與公眾平台一致

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        String signature = req.getParameter("signature");
        String timestamp = req.getParameter("timestamp");
        String nonce = req.getParameter("nonce");
        String echostr = req.getParameter("echostr");

        // 1. 排序
        String[] arr = {TOKEN, timestamp, nonce};
        Arrays.sort(arr);
        String sortedStr = String.join("", arr);

        // 2. SHA1
        String sha1 = SHA1Util.encode(sortedStr);

        // 3. 驗證
        if (sha1.equals(signature)) {
            resp.getWriter().print(echostr); // 關鍵:原樣返回
        } else {
            resp.getWriter().print("驗證失敗");
        }
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) {
        // 處理業務消息(文本、圖片、事件等),單獨篇幅講解
    }
}

SHA1 工具類


public class SHA1Util {
    public static String encode(String str) {
        try {
            MessageDigest md = MessageDigest.getInstance("SHA-1");
            byte[] digest = md.digest(str.getBytes(StandardCharsets.UTF_8));
            StringBuilder hex = new StringBuilder();
            for (byte b : digest) {
                String h = Integer.toHexString(b & 0xFF);
                if (h.length() == 1) hex.append('0');
                hex.append(h);
            }
            return hex.toString();
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
    }
}

Access Token 管理機制

核心規則

  • 唯一憑證:調用所有業務接口必須帶上 access_token。
  • 有效期 7200 秒 (2 小時):過期需重新獲取。
  • 每日調用上限 2000 次:不可高頻輪詢,必須緩存並主動刷新。
  • 重複獲取導致舊 token 失效:集群環境必須集中管理。

獲取接口

GET https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
// 返回: {"access_token":"ACCESS_TOKEN","expires_in":7200}

單機版管理方案:啟動 Servlet + 後台線程


@WebServlet(value = "/init", loadOnStartup = 0)
public class AccessTokenServlet extends HttpServlet {
    @Override
    public void init() {
        String appId = getInitParameter("appid");
        String appSecret = getInitParameter("appsecret");
        new Thread(new TokenRefresher(appId, appSecret), "Wechat-Token-Refresher").start();
    }
}

class TokenRefresher implements Runnable {
    private final String appId, appSecret;
    // volatile 保證可見性
    private volatile AccessTokenHolder holder = new AccessTokenHolder();

    TokenRefresher(String appId, String appSecret) { this.appId = appId; this.appSecret = appSecret; }

    @Override public void run() {
        while (true) {
            try {
                AccessToken token = fetchToken();
                if (token != null) {
                    holder = new AccessTokenHolder(token.getAccessToken(), System.currentTimeMillis() + token.getExpiresIn() * 1000L);
                    // 成功:休眠 7000 秒 (略小於 7200,留緩衝)
                    Thread.sleep(7000_000L);
                } else {
                    Thread.sleep(3_000L); // 失敗快速重試
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt(); break;
            } catch (Exception e) {
                log.error("Token 刷新異常", e);
                try { Thread.sleep(10_000L); } catch (InterruptedException ie) { break; }
            }
        }
    }

    private AccessToken fetchToken() {
        String url = String.format("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s", appId, appSecret);
        String json = HttpUtil.get(url); // 封裝 HttpsURLConnection
        return JSON.parseObject(json, AccessToken.class); // Fastjson/Gson
    }

    // 業務調用獲取 token
    static String getAccessToken() {
        return INSTANCE.holder.getToken();
    }
}

class AccessTokenHolder {
    final String token;
    final long expireTime; // 毫秒時間戳
    AccessTokenHolder() { this(null, 0); }
    AccessTokenHolder(String token, long expireTime) { this.token = token; this.expireTime = expireTime; }
    String getToken() { return (token != null && System.currentTimeMillis() < expireTime) ? token : null; }
}

web.xml 配置 (非 Spring Boot)


<servlet>
    <servlet-name>initToken</servlet-name>
    <servlet-class>com.xxx.AccessTokenServlet</servlet-class>
    <init-param><param-name>appid</param-name><param-value>你的AppID</param-value></init-param>
    <init-param><param-name>appsecret</param-name><param-value>你的AppSecret</param-value></init-param>
    <load-on-startup>0</load-on-startup>
</servlet>

集群/分佈式環境升級方案

參數說明
signature微信加密簽名
timestamp時間戳
nonce隨機數
echostr隨機字符串
方案關鍵點
Redis 集中存儲Key: wechat:access_token:{appid},Value: token,TTL 7000s。雙重檢查鎖獲取:無則單節點請求微信並回填。
分佈式鎖Redisson RLock 保證集群僅一節點刷新。
定時任務XXL-Job 或 Spring @Scheduled 定時刷新,比守護線程更可控。

HTTPS 請求工具類


public class HttpUtil {
    public static String get(String url) {
        try {
            URL u = new URL(url);
            HttpsURLConnection conn = (HttpsURLConnection) u.openConnection();
            // 信任所有證書 (生產環境請配置正規 CA 憑證)
            SSLContext ctx = SSLContext.getInstance("TLS");
            ctx.init(null, new TrustManager[]{new X509TrustManager() {
                public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; }
                public void checkClientTrusted(X509Certificate[] c, String a) {}
                public void checkServerTrusted(X509Certificate[] c, String a) {}
            }}, null);
            conn.setSSLSocketFactory(ctx.getSocketFactory());
            conn.setHostnameVerifier((h, s) -> true);
            conn.setRequestMethod("GET");
            conn.setConnectTimeout(5000);
            conn.setReadTimeout(5000);
            try (BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8))) {
                return br.lines().collect(Collectors.joining("\n"));
            }
        } catch (Exception e) {
            throw new RuntimeException("HTTPS GET 失敗: " + url, e);
        }
    }
}

測試驗證

  1. 配置公眾平台 URL: https://your.domain/wechat,Token 一致。
  2. 提交 → 顯示 "配置成功"。
  3. 控制台輸出 Token 刷新日誌。
  4. JSP/Controller 調用 TokenRefresher.getAccessToken() 獲取有效 token。

總結

微信公眾號開發基石在於:接入驗證邏輯正確 (SHA1+排序+原樣返回 echostr) 以及 Access Token 綠色管理 (緩存+定時刷新+集群互斥)。單機用 Servlet+守護線程快速起步,生產必上 Redis+分佈式鎖。掌握這兩點,後續菜單管理、素材上傳、模板消息、網頁授權等高級接口調用才有可靠基礎。

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