微信平台开发教程 (Java 版)
本教程将引导你完成一个完整的 Java 微信公众号后端服务开发,包括:

- 准备工作:注册公众号、获取必要信息。
- 环境搭建:创建 Java Web 项目。
- 核心概念:理解微信服务器与开发者服务器的交互模式。
- 接入验证:实现微信服务器的首次校验。
- 接收和发送消息:处理用户的各种输入(文本、图片、菜单点击等)并回复。
- 高级功能:如获取用户信息、网页授权等。
- 推荐工具与框架:提升开发效率。
第一章:准备工作
在开始编码之前,你必须完成以下准备工作:
1 注册微信公众号
你需要一个公众号来进行开发,主要分为两种:
- 订阅号:适合媒体和个人,主要功能是信息推送,认证后可以获取高级接口权限。
- 服务号:适合企业和组织,功能更强大,如微信支付、用户管理等。
对于初学者和学习目的,订阅号完全足够。
注册地址:https://mp.weixin.qq.com/

按照官网指引完成注册和认证(认证需要300元/年,但非必需,部分高级接口需要认证才能使用)。
2 获取开发者凭证
注册成功后,登录微信公众平台,在 开发 -> 基本配置 中找到你的:
- AppID (应用ID):公众号的唯一标识。
- AppSecret (应用密钥):与 AppID 配对使用,用于获取 Access Token,请务必保密!
3 选择服务器并配置
你需要一台公网可访问的服务器(可以是云服务器如阿里云、腾讯云,也可以是本地网络通过花生壳等工具实现公网穿透)。
在 开发 -> 基本配置 页面,点击 “配置” 按钮,填写以下信息:
- URL (服务器地址):你的 Java Web 应用的公网访问地址,
http://yourdomain.com/wechat。 - Token (令牌):可以任意填写,用作签名验证,
myWechatToken。 - EncodingAESKey (消息加解密密钥):可以随机生成,用于消息的安全传输,暂时可以不启用,选择“明文模式”。
- 消息加解密方式:选择 “明文模式” (Plaintext Mode),最简单,适合开发阶段。
配置完成后,微信服务器会向你的 URL 发送一个 GET 请求进行验证,这就是我们下一步要实现的 “接入验证”。
第二章:环境搭建
我们将使用最基础的 Servlet 技术来搭建项目,这样能让你清晰地理解微信交互的每一个细节,你也可以使用 Spring Boot 等更现代的框架。
1 创建 Java Web 项目
使用你喜欢的 IDE(如 IntelliJ IDEA 或 Eclipse)创建一个 Dynamic Web 项目。
2 添加依赖
在 pom.xml 文件中添加 Servlet API 依赖,如果你使用 Tomcat 10+,jakarta.servlet-api 是正确的;如果使用 Tomcat 9 及以下,则使用 javax.servlet-api。
<dependencies>
<!-- Servlet API (for Tomcat 10+) -->
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>5.0.0</version>
<scope>provided</scope>
</dependency>
<!-- 用于解析 XML 的工具,可选 -->
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.6.1</version>
</dependency>
</dependencies>
第三章:核心概念 - 消息交互流程
理解这个流程是开发的关键。
- 用户 在公众号里发送消息或点击菜单。
- 微信服务器 接收到消息后,将其封装成 XML 格式,POST 请求到你之前在后台配置的 URL。
- 你的服务器 (Java Web App) 接收到这个 POST 请求。
- 你的服务器 解析 XML,根据消息类型(文本、图片、事件等)进行业务处理。
- 你的服务器 将要回复的内容也按照特定的 XML 格式组装好。
- 你的服务器 将这个 XML 响应体作为 HTTP Response 返回给 微信服务器。
- 微信服务器 再将这个回复内容推送给 用户。
第四章:接入验证 (实现 GET 请求处理)
当你在微信后台配置并提交后,微信服务器会向你的 URL 发送一个 GET 请求,以验证你拥有服务器的控制权。
请求参数包含:
signature: 微信加密签名timestamp: 时间戳nonce: 随机数echostr: 随机字符串
你需要验证 signature 的有效性。
验证算法:
signature = SHA1( token + timestamp + nonce )
你的代码需要计算 SHA1,并与微信传来的 signature 进行比对,如果一致,说明请求来自微信,你需要将 echostr 原样返回。
1 创建 WeChatServlet
package com.example.servlet;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@WebServlet("/wechat") // 这个 URL 必须和微信后台配置的 URL 完全一致
public class WeChatServlet extends HttpServlet {
// 在微信后台配置的 Token
private final String TOKEN = "myWechatToken";
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1. 获取微信服务器 GET 请求的参数
String signature = req.getParameter("signature");
String timestamp = req.getParameter("timestamp");
String nonce = req.getParameter("nonce");
String echostr = req.getParameter("echostr");
// 2. 验证签名
if (checkSignature(signature, timestamp, nonce)) {
// 3. 验证成功,原样返回 echostr
PrintWriter out = resp.getWriter();
out.print(echostr);
out.close();
} else {
// 4. 验证失败,返回错误
resp.sendError(HttpServletResponse.SC_FORBIDDEN, "Invalid signature");
}
}
/**
* 验证签名
*/
private boolean checkSignature(String signature, String timestamp, String nonce) {
// 1. 将 token, timestamp, nonce 三个参数进行字典序排序
List<String> params = Arrays.asList(TOKEN, timestamp, nonce);
Collections.sort(params);
// 2. 将三个参数字符串拼接成一个字符串进行 SHA1 加密
StringBuilder sb = new StringBuilder();
for (String param : params) {
sb.append(param);
}
String temp = sb.toString();
// 3. 将加密后的字符串与 signature 对比
try {
MessageDigest md = MessageDigest.getInstance("SHA-1");
byte[] digest = md.digest(temp.getBytes());
String mySignature = bytesToHex(digest);
return mySignature.equals(signature);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return false;
}
}
/**
* 字节数组转十六进制字符串
*/
private String bytesToHex(byte[] bytes) {
StringBuilder hexString = new StringBuilder();
for (byte b : bytes) {
String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1) {
hexString.append('0');
}
hexString.append(hex);
}
return hexString.toString();
}
}
2 部署并测试
- 将你的项目打包成 WAR 文件并部署到你的服务器上。
- 确保
http://yourdomain.com/wechat可以访问到WeChatServlet。 - 回到微信后台,点击 “提交”,如果配置正确,页面会提示“成功”。
第五章:接收和发送消息 (实现 POST 请求处理)
我们来处理用户发来的消息,微信服务器会将消息以 XML 的形式 POST 到你的 Servlet。
1 定义消息接收和回复的 Java Bean (POJO)
为了方便解析和生成 XML,我们创建一些对应的 Java 类。
接收消息 (BaseMessage.java)
package com.example.model.in;
import jakarta.xml.bind.annotation.XmlElement;
import jakarta.xml.bind.annotation.XmlRootElement;
@XmlRootElement(name="xml")
public class BaseMessage {
@XmlElement
private String ToUserName; // 开发者微信号
@XmlElement
private String FromUserName; // 发送方帐号(OpenID)
@XmlElement
private long CreateTime; // 消息创建时间
@XmlElement
private String MsgType; // 消息类型 (text, image, event...)
// getters and setters...
}
文本消息 (TextMessage.java)
package com.example.model.in;
import jakarta.xml.bind.annotation.XmlElement;
public class TextMessage extends BaseMessage {
@XmlElement
private String Content; // 文本消息内容
// getters and setters...
// 可以重写 MsgType 为 "text"
}
回复消息 (BaseResponse.java)
package com.example.model.out;
import jakarta.xml.bind.annotation.XmlElement;
import jakarta.xml.bind.annotation.XmlRootElement;
@XmlRootElement(name="xml")
public class BaseResponse {
@XmlElement
private String ToUserName; // 接收方帐号(OpenID)
@XmlElement
private String FromUserName; // 开发者微信号
@XmlElement
private long CreateTime; // 消息创建时间
@XmlElement
private String MsgType; // 消息类型
// getters and setters...
}
文本回复消息 (TextResponse.java)
package com.example.model.out;
import jakarta.xml.bind.annotation.XmlElement;
public class TextResponse extends BaseResponse {
@XmlElement
private String Content; // 回复消息内容
// getters and setters...
// 可以重写 MsgType 为 "text"
}
2 修改 WeChatServlet 处理 POST 请求
我们需要一个 XML 解析库,dom4j 或 JAXB,这里我们用 dom4j。
确保 pom.xml 中有 dom4j 依赖。
然后修改 WeChatServlet:
package com.example.servlet;
// ... (imports)
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import com.example.model.in.TextMessage;
import com.example.model.out.TextResponse;
@WebServlet("/wechat")
public class WeChatServlet extends HttpServlet {
private final String TOKEN = "myWechatToken";
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1. 请求体乱码处理
req.setCharacterEncoding("UTF-8");
resp.setCharacterEncoding("UTF-8");
// 2. 解析微信服务器发送过来的 XML
try {
SAXReader reader = new SAXReader();
Document doc = reader.read(req.getInputStream());
Element root = doc.getRootElement();
// 3. 封装成 Java 对象
String toUserName = root.elementText("ToUserName");
String fromUserName = root.elementText("FromUserName");
String msgType = root.elementText("MsgType");
// 4. 根据消息类型进行处理
String responseXml = "";
if ("text".equals(msgType)) {
// 文本消息
String content = root.elementText("Content");
System.out.println("收到来自 " + fromUserName + " 的消息: " + content);
// 5. 创建回复
TextResponse textResponse = new TextResponse();
textResponse.setToUserName(fromUserName);
textResponse.setFromUserName(toUserName);
textResponse.setCreateTime(System.currentTimeMillis() / 1000);
textResponse.setMsgType("text");
textResponse.setContent("你发送的是: " + content);
// 6. 将回复对象转换为 XML 字符串 (这里简单拼接,实际应使用XML库)
responseXml = buildTextResponseXml(textResponse);
} else if ("event".equals(msgType)) {
// 事件推送,如关注、点击菜单等
String event = root.elementText("Event");
if ("subscribe".equals(event)) {
// 用户关注事件
TextResponse textResponse = new TextResponse();
textResponse.setToUserName(fromUserName);
textResponse.setFromUserName(toUserName);
textResponse.setCreateTime(System.currentTimeMillis() / 1000);
textResponse.setMsgType("text");
textResponse.setContent("感谢关注!");
responseXml = buildTextResponseXml(textResponse);
}
// 可以处理其他事件...
}
// 7. 返回响应
resp.getWriter().write(responseXml);
} catch (DocumentException e) {
e.printStackTrace();
}
}
/**
* 手动构建文本回复的 XML (简化版,生产环境建议使用模板或XML库)
*/
private String buildTextResponseXml(TextResponse response) {
return "<xml>" +
"<ToUserName><![CDATA[" + response.getToUserName() + "]]></ToUserName>" +
"<FromUserName><![CDATA[" + response.getFromUserName() + "]]></FromUserName>" +
"<CreateTime>" + response.getCreateTime() + "</CreateTime>" +
"<MsgType><![CDATA[text]]></MsgType>" +
"<Content><![CDATA[" + response.getContent() + "]]></Content>" +
"</xml>";
}
// ... (doGet 和 checkSignature 方法保持不变)
}
3 测试
用你的个人微信关注你的公众号,然后发送任何文本消息,你应该能收到服务器自动回复的“你发送的是: xxx”消息,如果你取消关注再重新关注,也应该收到欢迎消息。
第六章:高级功能示例
1 获取 Access Token
Access Token 是调用微信高级接口的凭证,有效期2小时,需要全局缓存。
获取地址:https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
实现一个 TokenService:
package com.example.service;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.concurrent.TimeUnit;
public class TokenService {
private static final String GET_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";
private static String accessToken;
private static long expireTime;
public static synchronized String getAccessToken() {
// 如果token不存在或已过期,则重新获取
if (accessToken == null || System.currentTimeMillis() > expireTime) {
try {
HttpClient client = HttpClient.newHttpClient();
String url = GET_TOKEN_URL.replace("APPID", "你的AppID").replace("APPSECRET", "你的AppSecret");
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url))
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
ObjectMapper mapper = new ObjectMapper();
JsonNode root = mapper.readTree(response.body());
accessToken = root.path("access_token").asText();
// 提前5分钟过期
expireTime = System.currentTimeMillis() + (root.path("expires_in").asLong() - 300) * 1000;
System.out.println("成功获取 Access Token: " + accessToken);
} catch (IOException | InterruptedException e) {
e.printStackTrace();
return null;
}
}
return accessToken;
}
}
注意:JsonNode 来自 jackson-databind,需要在 pom.xml 中添加依赖。
2 获取用户信息 (需要用户关注公众号)
接口地址:https://api.weixin.qq.com/cgi-bin/user/info?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN
你可以在接收到任何消息时,通过 FromUserName (即 OpenID) 来获取用户信息。
// 在你的业务逻辑中调用 String openId = fromUserName; String userInfoUrl = "https://api.weixin.qq.com/cgi-bin/user/info?access_token=" + TokenService.getAccessToken() + "&openid=" + openId + "&lang=zh_CN"; // 使用 HttpClient 发送请求并解析返回的 JSON,即可得到用户昵称、头像等信息
第七章:推荐工具与框架
手动处理 XML 和 HTTP 请求非常繁琐,在实际开发中,强烈建议使用成熟的第三方库或框架。
1 第三方 SDK (推荐)
这些 SDK 已经封装了所有微信 API 的调用逻辑,让你只需关注业务。
-
**WxJava (强烈推荐)**:国内最流行、功能最全的微信 Java 开发工具包,它支持公众号、小程序、企业微信、微信支付等几乎所有微信生态。
- GitHub: https://github.com/Wechat-Group/WxJava
- 特点: 文档完善,社区活跃,更新及时,能极大提升开发效率,你可以用几行代码就实现获取用户信息、发送模板消息等复杂功能。
-
JeecgBoot: 一个基于代码生成器的企业级快速开发平台,内置了微信端功能,如果你需要快速搭建一个管理系统,可以考虑。
2 使用 WxJava 重写示例
如果使用 WxJava,你的代码会变得非常简单:
-
添加依赖:
<dependency> <groupId>cn.binarywang</groupId> <artifactId>weixin-java-mp</artifactId> <version>4.4.0</version> <!-- 请使用最新版本 --> </dependency> -
配置 WxJava:
@Configuration public class WxMpConfig { @Value("${wx.mp.app-id}") private String appId; @Value("${wx.mp.secret}") private String secret; @Bean public WxMpService wxMpService() { WxMpService service = new WxMpServiceImpl(); service.setWxMpConfigStorage(new WxMpDefaultConfigStorage()); service.getWxMpConfigStorage().setAppid(appId); service.getWxMpConfigStorage().setSecret(secret); return service; } } -
创建 Controller 处理消息:
@RestController @RequestMapping("/wx") public class WxController { @Autowired private WxMpService wxMpService; @PostMapping(produces = "application/xml; charset=UTF-8") public String post(@RequestBody String requestBody, @RequestParam("signature") String signature, @RequestParam("timestamp") String timestamp, @RequestParam("nonce") String nonce, @RequestParam("openid") String openid, @RequestParam("encrypt_type") String encryptType, @RequestParam("msg_signature") String msgSignature) { // 1. 验证签名 if (!this.wxMpService.checkSignature(timestamp, nonce, signature)) { throw new IllegalArgumentException("非法请求,可能属于伪造的请求!"); } // 2. 解析消息 WxMpXmlMessage inMessage = WxMpXmlMessage.fromEncryptedXml(requestBody, this.wxMpService.getWxMpConfigStorage(), timestamp, nonce, msgSignature); System.out.println("接收消息: " + inMessage); // 3. 处理消息并创建回复 WxMpXmlOutMessage outMessage = handle(inMessage); if (outMessage == null) { return ""; } // 4. 加密并返回响应 return outMessage.toEncryptedXml(this.wxMpService.getWxMpConfigStorage()); } private WxMpXmlOutMessage handle(WxMpXmlMessage inMessage) { // 文本消息处理 if ("text".equals(inMessage.getMsgType())) { return WxMpXmlOutMessage.TEXT() .content("你发送的是: " + inMessage.getContent()) .fromUser(inMessage.getToUser()) .toUser(inMessage.getFromUser()) .build(); } // 关注事件处理 if ("event".equals(inMessage.getMsgType()) && "subscribe".equals(inMessage.getEvent())) { return WxMpXmlOutMessage.TEXT() .content("感谢关注!") .fromUser(inMessage.getToUser()) .toUser(inMessage.getFromUser()) .build(); } return null; } }可以看到,使用 WxJava 后,你几乎不需要手动处理 XML 和签名验证,代码变得非常清晰和易于维护。
本教程从零开始,为你展示了使用 Java 开发微信公众号后端的全过程:
- 从基础配置 开始,理解了微信服务器与开发者服务器的交互模式。
- 通过 Servlet 实现了最核心的接入验证和消息收发,让你对底层原理有了深刻理解。
- 介绍了 Access Token 和获取用户信息等高级功能。
- 强烈推荐了 WxJava 这样的优秀 SDK,并展示了如何使用它来简化开发。
对于任何严肃的项目,都请直接使用 WxJava,它将为你节省大量的时间和精力,并避免很多潜在的坑,而对于初学者,亲手实现一遍基础功能是非常有价值的,祝开发顺利!
