目录
- 准备工作
- 1 注册微信公众号
- 2 获取开发者凭证
- 3 选择开发模式
- 4 本地开发与外网访问
- 核心技术栈
- 1 Spring Boot (推荐)
- 2 Servlet API
- 3 HTTP 客户端
- 第一步:接入微信公众平台(验证服务器地址有效性)
- 1 原理讲解
- 2 代码实现
- 3 部署与测试
- 第二步:接收和回复用户消息
- 1 消息类型介绍
- 2 接收消息流程
- 3 解析 XML 消息
- 4 构造回复消息
- 5 完整代码示例(文本消息)
- 第三步:开发自定义菜单
- 1 获取 Access Token
- 2 创建菜单
- 3 代码实现
- 进阶与总结
- 1 消息加解密(安全模式)
- 2 常用 API 调用(获取用户信息、模板消息等)
- 3 推荐学习资源
准备工作
在开始写代码之前,你必须在微信公众平台完成以下准备工作。
1 注册微信公众号
- 访问 微信公众平台。
- 如果你没有公众号,需要先注册,对于个人开发者,注册 订阅号 即可,订阅号主要用于信息发布,功能上与公众号(服务号)略有不同,但开发模式完全一致。
- 完成邮箱验证、身份信息登记等流程。
2 获取开发者凭证
登录公众号后台,在 设置与开发 -> 基本配置 中找到以下信息,这是你的公众号在开发者模式下的“身份证”:
- AppID (应用ID): 公众号的唯一标识。
- AppSecret (应用密钥): 用于获取 Access Token,请务必保密,不要泄露。
3 选择开发模式
在 基本配置 页面,将服务器配置修改为 “开发者模式”,我们需要填写一个 URL 和一个 Token。
- URL (服务器地址): 你部署 Java Web 应用的公网访问地址,
https://yourdomain.com/wechat。 - Token (令牌): 可以任意填写,如
myWechatToken123,主要用于验证请求是否来自微信服务器。 - 消息加解密方式: 先选择 “明文模式”,便于开发和调试,后续生产环境可改为“安全模式”。
4 本地开发与外网访问
由于微信服务器需要通过公网 IP 访问你的服务,你不能直接在本地 localhost 上进行开发,你需要:
- 使用内网穿透工具(推荐新手)
- 购买云服务器
在阿里云、腾讯云等平台购买一台ECS,直接在上面部署你的应用。
核心技术栈
我们以目前最主流的 Spring Boot 为例进行讲解,因为它能极大地简化 Web 应用的开发,原理同样适用于传统的 Java Web 项目(使用 Servlet API)。
1 Spring Boot (推荐)
- 优点: 配置简单,内嵌 Tomcat 服务器,开箱即用。
- 创建项目: 使用 Spring Initializr 快速创建一个项目,添加
Spring Web依赖。
2 Servlet API
- 如果你使用传统的 Maven/Gradle 项目,需要手动添加
javax.servlet-api依赖。 - 核心是编写一个继承自
HttpServlet的类,并重写doGet和doPost方法。
3 HTTP 客户端
在调用微信 API(如创建菜单)时,需要发送 HTTP 请求,推荐使用成熟的库:
- OkHttp: 轻量级,性能好。
- Apache HttpClient: 功能强大,稳定。
- Spring RestTemplate: 如果是 Spring Boot 项目,自带且使用方便。
第一步:接入微信公众平台(验证服务器地址有效性)
这是开发的第一步,目的是让微信服务器确认你的服务器是“你自己的”。
1 原理讲解
当你在公众号后台提交 URL 和 Token 后,微信服务器会向你的 URL 发送一个 GET 请求,请求中包含四个参数:
signature: 微信加密签名timestamp: 时间戳nonce: 随机数echostr: 随机字符串
你的服务器需要做以下两件事来验证:
- 验证签名:将
token、timestamp、nonce三个参数进行字典序排序,然后拼接成一个字符串,进行SHA1加密,将加密后的字符串与signature对比,如果一致,则请求来自微信。 - 返回 echostr:如果签名验证通过,原样返回
echostr参数内容,表示接入成功,否则,返回空。
2 代码实现 (Spring Boot)
创建一个 Controller 来处理 /wechat 路径的请求。
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
@RestController
@RequestMapping("/wechat")
public class WeChatController {
// 在公众号后台配置的 Token
private final String TOKEN = "myWechatToken123";
@GetMapping
public void validate(HttpServletRequest request, HttpServletResponse response) throws IOException {
String signature = request.getParameter("signature");
String timestamp = request.getParameter("timestamp");
String nonce = request.getParameter("nonce");
String echostr = request.getParameter("echostr");
// 1. 校验签名
if (checkSignature(signature, timestamp, nonce)) {
// 2. 如果校验成功,返回 echostr
response.getWriter().write(echostr);
} else {
// 签名校验失败
response.getWriter().write("Invalid request");
}
}
/**
* 校验签名
*/
private boolean checkSignature(String signature, String timestamp, String nonce) {
String[] arr = new String[]{TOKEN, timestamp, nonce};
// 1. 将 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. 将 sha1 加密后的字符串可与 signature 对比
return tmpStr != null && tmpStr.equals(signature);
}
/**
* SHA1 加密
*/
private String SHA1(String decript) {
try {
MessageDigest digest = MessageDigest.getInstance("SHA-1");
digest.update(decript.getBytes());
byte[] messageDigest = digest.digest();
// Create Hex String
StringBuilder hexString = new StringBuilder();
for (byte aMessageDigest : messageDigest) {
String shaHex = Integer.toHexString(aMessageDigest & 0xff);
if (shaHex.length() < 2) {
hexString.append(0);
}
hexString.append(shaHex);
}
return hexString.toString();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return "";
}
}
3 部署与测试
- 启动你的 Spring Boot 应用。
- 确保内网穿透工具或云服务器配置正确,使得
https://yourdomain.com/wechat可以访问。 - 在公众号后台的 基本配置 页面,填写 URL 和 Token,点击 “提交”。
- 如果提示“成功”,则表示服务器接入验证完成!
第二步:接收和回复用户消息
接入成功后,用户在公众号里发送的任何消息,微信服务器都会通过你配置的 URL 以 POST 请求的形式推送到你的服务器。
1 消息类型介绍
用户发送的消息和公众号回复的消息都有多种格式,最常见的是 文本消息。
- 用户发送的消息类型:
text: 文本消息image: 图片消息voice: 语音消息video: 视频消息shortvideo: 小视频消息location: 地理位置消息link: 链接消息
- 公众号回复的消息类型:
text: 文本回复image: 图片回复voice: 语音回复video: 视频回复music: 音乐回复news: 图文消息回复(多条)
2 接收消息流程
- 用户在公众号输入并发送消息。
- 微信服务器将消息打包成 XML 格式,通过
POST请求发送到你的服务器 URL。 - 你的服务器解析 XML,并根据消息内容进行处理。
- 你的服务器在 5 秒内,将回复内容也打包成 XML 格式,通过 HTTP 响应返回给微信服务器。
- 微信服务器再将你的回复推送给用户。
3 解析 XML 消息
微信发送过来的消息是 XML 格式,我们可以使用 Java 的 DOM、SAX 或第三方库(如 dom4j)来解析。
以文本消息为例,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 结构。
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement(name = "xml")
public class TextMessage {
private String ToUserName;
private String FromUserName;
private long CreateTime;
private String MsgType;
private String Content;
private long MsgId;
// Getters and Setters
// ... (省略,实际开发中需要为每个字段添加getter和setter)
}
然后在 Controller 中使用 JAXB(Java Architecture for XML Binding)来解析请求体。
4 构造回复消息
回复消息同样是 XML 格式,回复一条文本消息:
<xml> <ToUserName><![CDATA[fromUser]]></ToUserName> <FromUserName><![CDATA[toUser]]></FromUserName> <CreateTime>1348831860</CreateTime> <MsgType><![CDATA[text]]></MsgType> <Content><![CDATA[你好,这是自动回复!]]></Content> </xml>
注意:ToUserName 和 FromUserName 的值与接收到的消息正好相反。
5 完整代码示例(文本消息)
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;
import java.io.IOException;
import java.io.InputStream;
@RestController
@RequestMapping("/wechat")
public class WeChatController {
private final String TOKEN = "myWechatToken123";
// ... checkSignature 和 SHA1 方法与上面相同 ...
@GetMapping
public void validate(HttpServletRequest request, HttpServletResponse response) throws IOException {
// ... 与上面相同 ...
}
@PostMapping
public void handlePost(HttpServletRequest request, HttpServletResponse response) throws Exception {
// 1. 解析微信服务器发来的 XML 消息
InputStream is = request.getInputStream();
JAXBContext jaxbContext = JAXBContext.newInstance(TextMessage.class);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
TextMessage inMessage = (TextMessage) unmarshaller.unmarshal(is);
// 2. 构造回复消息
TextMessage outMessage = new TextMessage();
outMessage.setToUserName(inMessage.getFromUserName());
outMessage.setFromUserName(inMessage.getToUserName());
outMessage.setCreateTime(System.currentTimeMillis() / 1000);
outMessage.setMsgType("text");
// 根据接收到的内容,设置回复内容
if ("你好".equals(inMessage.getContent())) {
outMessage.setContent("你好,世界!");
} else {
outMessage.setContent("我收到了你的消息: " + inMessage.getContent());
}
// 3. 将回复消息转换为 XML 并返回
response.setContentType("text/xml; charset=UTF-8");
JAXBContext jaxbContextOut = JAXBContext.newInstance(TextMessage.class);
jaxbContextOut.createMarshaller().marshal(outMessage, response.getWriter());
}
}
注意: 上述代码简化了 JAXB 的使用,实际项目中,你可能需要处理更复杂的 XML 映射或使用 dom4j。JAXB 默认不会处理 CDATA,你可能需要自定义适配器或手动拼接字符串来生成符合微信格式的 XML。
第三步:开发自定义菜单
自定义菜单可以让公众号界面更友好,方便用户交互,创建菜单需要调用微信的接口,并获取 access_token。
1 获取 Access Token
access_token 是公众号的全局唯一接口调用凭据,有效期 2 小时,获取方式是向微信服务器发送一个 GET 请求。
请求 URL: https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
2 创建菜单
获取到 access_token 后,就可以用它来创建菜单,需要向 https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN 发送一个 POST 请求,请求体是菜单的 JSON 结构。
3 代码实现
这里我们使用 Spring Boot 的 RestTemplate 来调用微信 API。
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.util.HashMap;
import java.util.Map;
@RestController
public class MenuController {
private final String APPID = "你的AppID";
private final String APPSECRET = "你的AppSecret";
private final String MENU_CREATE_URL = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token=";
private final String TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + APPID + "&secret=" + APPSECRET;
private RestTemplate restTemplate = new RestTemplate();
@PostMapping("/createMenu")
public String createMenu() {
// 1. 获取 access_token
Map<String, Object> tokenMap = restTemplate.getForObject(TOKEN_URL, Map.class);
String accessToken = (String) tokenMap.get("access_token");
if (accessToken == null) {
return "获取 access_token 失败";
}
// 2. 构造菜单 JSON
Map<String, Object> menuButton1 = new HashMap<>();
menuButton1.put("name", "点击一下");
menuButton1.put("type", "click");
menuButton1.put("key", "V1001_TODAY_MUSIC");
Map<String, Object> menuButton2 = new HashMap<>();
menuButton2.put("name", "跳转网页");
menuButton2.put("type", "view");
menuButton2.put("url", "https://www.example.com");
Map<String, Object> subButton = new HashMap<>();
subButton.put("name", "菜单");
subButton.put("sub_button", new Object[]{menuButton1, menuButton2});
Map<String, Object> menu = new HashMap<>();
menu.put("button", new Object[]{subButton});
// 3. 发送 POST 请求创建菜单
String result = restTemplate.postForObject(MENU_CREATE_URL + accessToken, menu, String.class);
return "创建菜单结果: " + result;
}
}
注意: 菜单的 JSON 结构非常严格,一个字符都不能错,你可以先在微信公众平台手动创建一个菜单,然后通过 自定义菜单查询接口 (https://api.weixin.qq.com/cgi-bin/menu/get?access_token=ACCESS_TOKEN) 来获取正确的 JSON 结构作为模板。
进阶与总结
1 消息加解密(安全模式)
当公众号后台配置为“安全模式”时,微信发送过来的消息是经过加密的,你需要:
- 下载微信官方提供的 加解密库(如
wxBot或WxJava项目中的相关模块)。 - 在你的服务器上保存
EncodingAESKey(在公众号后台获取)。 - 使用库提供的工具类来解密收到的消息,并在回复时加密。
2 常用 API 调用
除了创建菜单,微信还提供了丰富的 API,
- 用户管理: 获取用户信息、关注/取消关注事件等。
- 素材管理: 上传和获取图片、语音等素材。
- 模板消息: 发送服务通知,如订单确认、物流更新等。
- 客服消息: 7天内,可以给用户发特定类型的消息。
调用这些 API 的流程都类似:获取 access_token -> 拼接 API URL -> 发送 HTTP 请求(GET/POST)-> 处理返回结果。
3 推荐学习资源
- 微信官方文档: 最权威、最全面的资料,所有开发细节都在这里。
- WxJava 开源项目: 一个功能非常强大的 Java 微信开发 SDK,封装了几乎所有微信 API,能极大简化你的开发工作,如果你觉得从零开始写很麻烦,直接使用它是最好的选择。
- 微信 JS-SDK: 如果你的公众号需要嵌入 H5 页面,并调用微信的分享、拍照、扫一扫等功能,就需要用到 JS-SDK。
Java 微信公众号开发的核心在于:
- 理解微信的通信机制(验证、消息推送、API调用)。
- 搭建一个能接收和响应 HTTP 请求的 Web 服务(Spring Boot 是最佳选择)。
- 熟练处理 XML 和 JSON 数据的解析与构造。
- 安全地管理你的 AppSecret,并正确获取和使用
access_token。
希望这份详细的教程能帮助你顺利开启 Java 微信公众号的开发之旅!
