杰瑞科技汇

Java如何调用微信公众平台API?

这会是一个从入门到实践的详细指南,涵盖了环境准备、API调用、接收和发送消息、以及一个完整的实战示例。

Java如何调用微信公众平台API?-图1
(图片来源网络,侵删)

目录

  1. 准备工作
    • 注册微信公众平台账号
    • 获取开发者凭证
    • 配置服务器
  2. Java 技术栈选型
    • 核心框架
    • HTTP 客户端
    • XML/JSON 处理
    • 开发工具
  3. 核心概念:消息交互流程
  4. Java 实战:搭建一个完整的微信服务
    • 第一步:验证服务器地址 (Token 验证)
    • 第二步:接收用户消息 (XML 解析)
    • 第三步:回复用户消息 (XML 拼接)
    • 第四步:封装工具类 (调用微信 API)
  5. 常用 API 调用示例
    • 获取 Access Token
    • 获取用户信息
    • 发送客服消息
  6. 进阶与最佳实践
    • 使用缓存管理 Access Token
    • 使用 Spring Boot 简化开发
    • 安全性考虑

准备工作

在写任何 Java 代码之前,你必须先在微信公众平台完成配置。

a. 注册账号

  • 订阅号:适合个人、媒体、企业组织,主要用于信息发布和用户互动。
  • 服务号:适合企业、政府、媒体等组织,功能更强大,支持微信支付、高级接口等。
  • 测试号:推荐开发者使用,用于开发和测试,拥有大部分接口权限,无需审核,非常方便。

b. 获取开发者凭证

登录微信公众平台(或测试号平台),在 基本配置 -> 开发者设置 中找到:

  • AppID (应用ID):公众号的唯一标识。
  • AppSecret (应用密钥):用于获取 Access Token,请务必保密。

c. 配置服务器 (关键步骤)

这是连接你的 Java 后端和微信服务器的桥梁。

  1. 基本配置 -> 服务器配置 中点击 修改配置
  2. URL:填写你的 Java 服务器对外可访问的地址。http://yourdomain.com/wechat,微信服务器会向这个地址发送 POST 请求。
  3. Token:可以任意填写,用于验证请求是否来自微信服务器,你的 Java 代码里需要使用这个 Token。
  4. EncodingAESKey:用于消息加解密,可以随机生成,也可以留空(使用明文模式)。
  5. 消息加解密方式:选择 安全模式兼容模式,测试阶段可以先选 明文模式
  6. 点击 提交,微信服务器会向你填写的 URL 发送一个 GET 请求,你需要用 Java 代码正确响应这个请求才能通过验证。

Java 技术栈选型

对于微信后端开发,技术栈非常灵活。

Java如何调用微信公众平台API?-图2
(图片来源网络,侵删)
技术类别 推荐选择 理由
核心框架 Spring Boot 强烈推荐,简化配置,内嵌 Tomcat,能快速搭建和运行 Web 应用。
原生 Servlet 适用于学习原理或项目已有 Servlet 基础。
HTTP 客户端 OkHttp 轻量、高效、易用,是 Java 调用 HTTP API 的首选。
Apache HttpClient 功能强大,但配置稍显复杂。
Spring RestTemplate Spring 生态内置,在 Spring Boot 项目中很方便。
XML/JSON 处理 Jackson Spring Boot 默认集成,处理 JSON 非常方便。
Dom4J / XStream 微信消息交互主要使用 XML,这两个库是 Java 中处理 XML 的利器。
开发工具 IntelliJ IDEA 对 Java Web 和 Spring Boot 支持最好。

核心概念:消息交互流程

理解这个流程是开发的关键。

  1. 用户 在公众号里发送消息。
  2. 微信服务器 接收到消息,然后将它打包成一个 XML 格式的 POST 请求,发送到你之前配置的 服务器 URL
  3. 你的 Java 后端 接收到这个 POST 请求。
  4. 你的 Java 代码
    • 解析 XML 请求,获取消息类型(文本、图片、点击事件等)、发送者 OpenID 等。
    • 根据业务逻辑,决定如何回复。
    • 打包成特定格式的 XML。
    • 将这个 XML 响应返回给 微信服务器
  5. 微信服务器 收到你的 XML 响应后,再将其转发给 用户

Java 实战:搭建一个完整的微信服务

我们以 Spring Boot 为例,一步步搭建。

第一步:创建 Spring Boot 项目

使用 Spring Initializr 快速创建一个项目,添加 Spring Web 依赖。

第二步:验证服务器地址 (Token 验证)

当你在微信后台配置服务器时,微信会向你发送一个 GET 请求,参数包含 signature, timestamp, nonce, echostr,你需要用 tokentimestamp, nonce 进行排序并加密,然后与 signature 比较,如果一致,则返回 echostr

Java如何调用微信公众平台API?-图3
(图片来源网络,侵删)

WeChatController.java

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RequestParam;
import java.security.MessageDigest;
import java.util.Arrays;
@RestController
public class WeChatController {
    // 在微信后台配置的 Token
    private final String TOKEN = "your_wechat_token";
    // 处理微信服务器的验证请求 (GET)
    @GetMapping("/wechat")
    public String validate(@RequestParam("signature") String signature,
                          @RequestParam("timestamp") String timestamp,
                          @RequestParam("nonce") String nonce,
                          @RequestParam("echostr") String echostr) {
        // 1. 将 token, timestamp, nonce 三个参数进行字典序排序
        String[] arr = {TOKEN, timestamp, nonce};
        Arrays.sort(arr);
        // 2. 将三个参数字符串拼接成一个字符串进行 sha1 加密
        StringBuilder content = new StringBuilder();
        for (String s : arr) {
            content.append(s);
        }
        String tmpStr = sha1(content.toString());
        // 3. 将加密后的字符串与 signature 对比
        if (tmpStr != null && tmpStr.equals(signature)) {
            // 验证成功,原样返回 echostr
            return echostr;
        } else {
            // 验证失败
            return "error";
        }
    }
    // SHA1 加密方法
    private String sha1(String str) {
        try {
            MessageDigest digest = MessageDigest.getInstance("SHA-1");
            digest.update(str.getBytes());
            byte[] messageDigest = digest.digest();
            StringBuilder hexString = new StringBuilder();
            for (byte b : messageDigest) {
                String shaHex = Integer.toHexString(b & 0xFF);
                if (shaHex.length() < 2) {
                    hexString.append(0);
                }
                hexString.append(shaHex);
            }
            return hexString.toString();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    // 处理用户发送的消息 (POST)
    @PostMapping("/wechat")
    public String handleWeChatMessage(@RequestBody String requestBody) {
        // TODO: 下一步实现
        System.out.println("收到请求体: " + requestBody);
        return "success"; // 先返回成功,避免微信服务器重复推送
    }
}

启动你的 Spring Boot 应用,将 http://localhost:8080/wechat 填入微信后台配置,如果配置正确,验证会通过。

第三步:接收用户消息 (XML 解析)

用户发送消息后,微信服务器会发送一个 POST 请求,请求体是 XML,我们需要解析它。

微信发送的文本消息示例 XML:

<xml>
  <ToUserName><![CDATA[toUser]]></ToUserName>
  <FromUserName><![CDATA[fromUser]]></FromUserName>
  <CreateTime>1348831860</CreateTime>
  <MsgType><![CDATA[text]]></MsgType>
  <Content><![CDATA[this is a test]]></Content>
  <MsgId>1234567890123456</MsgId>
</xml>

我们可以创建一个 Java 类来映射这个 XML 结构。

WeChatMessage.java (使用 Dom4J 解析)

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.io.StringReader;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class WeChatMessageParser {
    public static Map<String, String> parse(String xml) {
        Map<String, String> map = new HashMap<>();
        try {
            SAXReader reader = new SAXReader();
            Document document = reader.read(new StringReader(xml));
            Element root = document.getRootElement();
            List<Element> elements = root.elements();
            for (Element e : elements) {
                map.put(e.getName(), e.getTextTrim());
            }
        } catch (DocumentException e) {
            e.printStackTrace();
        }
        return map;
    }
}

现在修改 WeChatControllerhandleWeChatMessage 方法:

@PostMapping("/wechat")
public String handleWeChatMessage(@RequestBody String requestBody) {
    // 1. 解析 XML
    Map<String, String> messageMap = WeChatMessageParser.parse(requestBody);
    String fromUser = messageMap.get("FromUserName");
    String toUser = messageMap.get("ToUserName");
    String msgType = messageMap.get("MsgType");
    String content = messageMap.get("Content");
    System.out.println("From: " + fromUser);
    System.out.println("Content: " + content);
    // 2. 根据消息类型处理 (这里只处理文本消息)
    if ("text".equals(msgType)) {
        String replyContent = "你发送的是: " + content;
        // 3. 构造回复消息的 XML
        String replyXml = buildReplyXml(fromUser, toUser, replyContent);
        return replyXml;
    }
    return "success"; // 其他消息类型暂时返回成功
}
// 构造回复文本消息的 XML
private String buildReplyXml(String fromUser, String toUser, String content) {
    return "<xml>" +
            "<ToUserName><![CDATA[" + fromUser + "]]></ToUserName>" +
            "<FromUserName><![CDATA[" + toUser + "]]></FromUserName>" +
            "<CreateTime>" + System.currentTimeMillis() / 1000 + "</CreateTime>" +
            "<MsgType><![CDATA[text]]></MsgType>" +
            "<Content><![CDATA[" + content + "]]></Content>" +
            "</xml>";
}

当你关注测试号并发送文本时,你的 Java 应用应该能收到消息,并回复给你。

第四步:封装工具类 (调用微信 API)

开发中经常需要调用微信 API,如获取 Access Token、获取用户信息等,我们把这些操作封装成工具类。

WeChatApiUtil.java

import com.fasterxml.jackson.databind.ObjectMapper;
import okhttp3.*;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.TimeUnit;
public class WeChatApiUtil {
    private static final OkHttpClient CLIENT = new OkHttpClient.Builder()
            .connectTimeout(10, TimeUnit.SECONDS)
            .writeTimeout(10, TimeUnit.SECONDS)
            .readTimeout(30, TimeUnit.SECONDS)
            .build();
    private static final ObjectMapper MAPPER = new ObjectMapper();
    private static final String BASE_URL = "https://api.weixin.qq.com/cgi-bin/";
    // 获取 Access Token
    public static String getAccessToken(String appId, String appSecret) {
        String url = BASE_URL + "token?grant_type=client_credential&appid=" + appId + "&secret=" + appSecret;
        Request request = new Request.Builder().url(url).build();
        try (Response response = CLIENT.newCall(request).execute()) {
            if (response.isSuccessful()) {
                String responseBody = response.body().string();
                Map<String, Object> map = MAPPER.readValue(responseBody, Map.class);
                return (String) map.get("access_token");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
    // 获取用户信息
    public static Map<String, Object> getUserInfo(String accessToken, String openId) {
        String url = BASE_URL + "user/info?access_token=" + accessToken + "&openid=" + openId;
        Request request = new Request.Builder().url(url).build();
        try (Response response = CLIENT.newCall(request).execute()) {
            if (response.isSuccessful()) {
                String responseBody = response.body().string();
                return MAPPER.readValue(responseBody, Map.class);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
    // ... 其他API调用方法,如发送客服消息等
}

使用示例:

String appId = "your_app_id";
String appSecret = "your_app_secret";
String accessToken = WeChatApiUtil.getAccessToken(appId, appSecret);
if (accessToken != null) {
    // 假设从消息中获取了用户的 openId
    String userOpenId = "some_open_id";
    Map<String, Object> userInfo = WeChatApiUtil.getUserInfo(accessToken, userOpenId);
    System.out.println("用户昵称: " + userInfo.get("nickname"));
}

常用 API 调用示例

a. 获取 Access Token

这是调用所有高级接口的前提。注意:Access Token 有效期为 2 小时,必须缓存起来,避免频繁请求。

// 在 WeChatApiUtil 中已经实现
String accessToken = WeChatApiUtil.getAccessToken(APP_ID, APP_SECRET);

b. 发送客服消息

当用户发送消息后,除了被动回复,还可以主动发送消息(如菜单点击后发送图文)。

// 在 WeChatApiUtil 中添加
public static void sendCustomerServiceMessage(String accessToken, String openId, String content) {
    String url = BASE_URL + "message/custom/send?access_token=" + accessToken;
    // 构造消息体 JSON
    Map<String, Object> message = new HashMap<>();
    Map<String, String> text = new HashMap<>();
    text.put("content", content);
    message.put("touser", openId);
    message.put("msgtype", "text");
    message.put("text", text);
    RequestBody body = RequestBody.create(
            MediaType.parse("application/json; charset=utf-8"),
            MAPPER.writeValueAsString(message)
    );
    Request request = new Request.Builder().url(url).post(body).build();
    try (Response response = CLIENT.newCall(request).execute()) {
        System.out.println("发送客服消息响应: " + response.body().string());
    } catch (IOException e) {
        e.printStackTrace();
    }
}

进阶与最佳实践

a. 使用缓存管理 Access Token

频繁获取 Access Token 会被微信限制,必须使用缓存(如 Redis、Guava Cache)来存储它。

使用 Guava Cache 示例:

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import java.util.concurrent.TimeUnit;
public class AccessTokenCache {
    private static final Cache<String, String> CACHE = CacheBuilder.newBuilder()
            .maximumSize(100) // 最多缓存100个token (如果管理多个公众号)
            .expireAfterWrite(110, TimeUnit.MINUTES) // 110分钟后过期,留出10分钟缓冲
            .build();
    public static String getAccessToken(String appId, String appSecret) {
        return CACHE.get(appId, () -> {
            System.out.println("缓存未命中,正在从微信服务器获取新的 Access Token...");
            return WeChatApiUtil.getAccessToken(appId, appSecret);
        });
    }
}

调用时直接使用 AccessTokenCache.getAccessToken(...) 即可。

b. 使用 Spring Boot 简化开发

Spring Boot 的自动配置和 Starter POMs 能极大简化工作。

  • Web 依赖spring-boot-starter-web 内嵌了 Tomcat 和 Spring MVC。
  • JSON 处理spring-boot-starter-json (Jackson) 自动配置。
  • HTTP 客户端:可以使用 RestTemplate (已自动配置) 或 WebClient (响应式)。

c. 安全性考虑

  • IP 白名单:在微信后台 基本配置 -> 公众号设置 中,将你的服务器 IP 添加到白名单,防止非法请求。
  • 验证签名:我们已经在第一步实现了对微信服务器请求的签名验证。
  • 加密解密:如果选择了安全模式,所有收发消息都需要进行 AES 加密解密,可以使用 WxJava 等开源库来简化这部分复杂的工作。

使用 Java 开发微信公众号后端,核心在于:

  1. 理解微信的消息交互流程
  2. 搭建一个能够接收和响应 HTTP 请求的 Web 服务(Spring Boot 是最佳选择)。
  3. 熟练处理 XML 格式的消息收发
  4. 封装好 API 调用工具,并注意 Access Token 的缓存

希望这份详细的指南能帮助你顺利开始 Java 微信公众号的开发之旅!

分享:
扫描分享到社交APP
上一篇
下一篇