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

目录
- 准备工作
- 注册微信公众平台账号
- 获取开发者凭证
- 配置服务器
- Java 技术栈选型
- 核心框架
- HTTP 客户端
- XML/JSON 处理
- 开发工具
- 核心概念:消息交互流程
- Java 实战:搭建一个完整的微信服务
- 第一步:验证服务器地址 (Token 验证)
- 第二步:接收用户消息 (XML 解析)
- 第三步:回复用户消息 (XML 拼接)
- 第四步:封装工具类 (调用微信 API)
- 常用 API 调用示例
- 获取 Access Token
- 获取用户信息
- 发送客服消息
- 进阶与最佳实践
- 使用缓存管理 Access Token
- 使用 Spring Boot 简化开发
- 安全性考虑
准备工作
在写任何 Java 代码之前,你必须先在微信公众平台完成配置。
a. 注册账号
- 订阅号:适合个人、媒体、企业组织,主要用于信息发布和用户互动。
- 服务号:适合企业、政府、媒体等组织,功能更强大,支持微信支付、高级接口等。
- 测试号:推荐开发者使用,用于开发和测试,拥有大部分接口权限,无需审核,非常方便。
- 地址:微信公众平台测试号
b. 获取开发者凭证
登录微信公众平台(或测试号平台),在 基本配置 -> 开发者设置 中找到:
- AppID (应用ID):公众号的唯一标识。
- AppSecret (应用密钥):用于获取 Access Token,请务必保密。
c. 配置服务器 (关键步骤)
这是连接你的 Java 后端和微信服务器的桥梁。
- 在
基本配置->服务器配置中点击修改配置。 - URL:填写你的 Java 服务器对外可访问的地址。
http://yourdomain.com/wechat,微信服务器会向这个地址发送 POST 请求。 - Token:可以任意填写,用于验证请求是否来自微信服务器,你的 Java 代码里需要使用这个 Token。
- EncodingAESKey:用于消息加解密,可以随机生成,也可以留空(使用明文模式)。
- 消息加解密方式:选择
安全模式或兼容模式,测试阶段可以先选明文模式。 - 点击
提交,微信服务器会向你填写的 URL 发送一个 GET 请求,你需要用 Java 代码正确响应这个请求才能通过验证。
Java 技术栈选型
对于微信后端开发,技术栈非常灵活。

| 技术类别 | 推荐选择 | 理由 |
|---|---|---|
| 核心框架 | 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 支持最好。 |
核心概念:消息交互流程
理解这个流程是开发的关键。
- 用户 在公众号里发送消息。
- 微信服务器 接收到消息,然后将它打包成一个 XML 格式的 POST 请求,发送到你之前配置的 服务器 URL。
- 你的 Java 后端 接收到这个 POST 请求。
- 你的 Java 代码:
- 解析 XML 请求,获取消息类型(文本、图片、点击事件等)、发送者 OpenID 等。
- 根据业务逻辑,决定如何回复。
- 打包成特定格式的 XML。
- 将这个 XML 响应返回给 微信服务器。
- 微信服务器 收到你的 XML 响应后,再将其转发给 用户。
Java 实战:搭建一个完整的微信服务
我们以 Spring Boot 为例,一步步搭建。
第一步:创建 Spring Boot 项目
使用 Spring Initializr 快速创建一个项目,添加 Spring Web 依赖。
第二步:验证服务器地址 (Token 验证)
当你在微信后台配置服务器时,微信会向你发送一个 GET 请求,参数包含 signature, timestamp, nonce, echostr,你需要用 token 和 timestamp, nonce 进行排序并加密,然后与 signature 比较,如果一致,则返回 echostr。

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;
}
}
现在修改 WeChatController 的 handleWeChatMessage 方法:
@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 开发微信公众号后端,核心在于:
- 理解微信的消息交互流程。
- 搭建一个能够接收和响应 HTTP 请求的 Web 服务(Spring Boot 是最佳选择)。
- 熟练处理 XML 格式的消息收发。
- 封装好 API 调用工具,并注意 Access Token 的缓存。
希望这份详细的指南能帮助你顺利开始 Java 微信公众号的开发之旅!
