目录
- 基础准备:了解微信公众号
- 1 公众号的类型
- 2 开发者模式
- 环境搭建:你的第一个 Java 项目
- 1 技术栈选择
- 2 创建 Spring Boot 项目
- 3 配置服务器信息
- 核心功能一:接收和回复用户消息
- 1 消息类型与数据格式
- 2 接收消息(服务器配置验证)
- 3 接收用户消息
- 4 回复用户消息
- 核心功能二:被动回复与主动发送
- 1 被动回复用户消息(Echo)
- 2 主动发送消息(客服消息)
- 核心功能三:获取 Access Token
- 1 什么是 Access Token
- 2 获取 Token 的正确姿势(缓存机制)
- 核心功能四:自定义菜单
- 1 菜单结构
- 2 创建菜单
- 进阶功能:网页授权获取用户信息
- 1 OAuth2.0 授权流程
- 2 实现网页授权
- 总结与最佳实践
- 1 推荐的第三方 SDK
- 2 消息加解密
- 3 异步处理
- 4 部署上线
基础准备:了解微信公众号
在开始编码前,你必须先理解微信公众号的基本概念。

1 公众号的类型
- 订阅号:主要用于信息传播,适合媒体、个人等,每天可以推送一次消息,消息在订阅号文件夹中。
- 服务号:主要为用户提供服务,适合企业、政府等,每月可以推送四次消息,消息会直接出现在聊天列表中,并可使用更多高级接口(如微信支付、JS-SDK等)。
- 小程序:一个独立的应用形态,不是公众号,但可以与公众号关联。
对于开发者来说,最重要的是:你必须是该公众号的“开发者”,并拥有 AppID 和 AppSecret。
2 开发者模式
微信提供了两种模式来处理用户消息:
- 编辑模式:通过网页可视化配置,适合非开发者,无法进行复杂的逻辑处理。
- 开发模式:开发者通过自己的服务器来接收和处理所有用户消息,并进行回复,这是我们本教程的重点。
要开启开发模式,你需要一个公网可以访问的服务器地址(URL),用于接收微信服务器发来的消息。
环境搭建:你的第一个 Java 项目
1 技术栈选择
- 框架:Spring Boot (强烈推荐),它能极大地简化项目配置和依赖管理。
- 构建工具:Maven 或 Gradle。
- 服务器:内嵌 Tomcat (由 Spring Boot 提供)。
- JSON 处理:Jackson (Spring Boot 默认集成)。
2 创建 Spring Boot 项目
最简单的方式是使用 Spring Initializr:

- Project: Maven Project
- Language: Java
- Spring Boot: 选择一个稳定版本 (如 2.7.x 或 3.x.x)
- Project Metadata:
- Group:
com.example - Artifact:
wechat-demo - Name:
wechat-demo - Packaging: Jar
- Java: 8 或更高版本
- Group:
- Dependencies: 添加
Spring Web依赖。 - 点击 "GENERATE" 下载项目压缩包,并用你的 IDE (如 IntelliJ IDEA 或 Eclipse) 打开。
3 配置服务器信息
- 登录 微信公众平台。
- 进入 “设置与开发” -> “基本配置”。
- 找到 “服务器配置”,点击 “修改配置”。
- 填写以下信息:
- URL: 这是你的服务器地址,必须公网可访问。
http://yourdomain.com/wechat/yourToken,我们稍后会创建这个接口。 - Token: 由你自定义的字符串,用于验证请求是否来自微信服务器。
- EncodingAESKey: 随机生成或手动填写,用于消息加解密,初期可以先留空,选择 “安全模式” 或 “兼容模式”。
- 消息加解密方式: 选择 “安全模式” 或 “兼容模式”。
- URL: 这是你的服务器地址,必须公网可访问。
- 点击 “提交”,微信服务器会向你的 URL 发送一个 GET 请求,你需要编写代码来响应这个请求以验证配置。
核心功能一:接收和回复用户消息
1 消息类型与数据格式
微信和你的服务器之间通过 XML 格式的数据进行通信。
-
接收到的消息类型:
text: 文本消息image: 图片消息voice: 语音消息video: 视频消息shortvideo: 小视频消息location: 地理位置消息link: 链接消息event: 事件推送(如关注、取消关注、点击菜单等)
-
回复的消息类型:
text: 文本消息image: 图片消息voice: 语音消息video: 视频消息music: 音乐消息news: 图文消息(多条)
2 接收消息(服务器配置验证)
当你在微信后台提交配置时,微信服务器会向你填写的 URL 发送一个 GET 请求,参数包含 signature, timestamp, nonce, echostr。

你需要做三件事来验证:
- 将
token、timestamp、nonce三个参数进行字典序排序。 - 将三个参数字符串拼接成一个字符串进行
SHA1加密。 - 将加密后的字符串与
signature对比,若相等,则请求来自微信。
代码实现 (创建一个 Controller):
// WeChatController.java
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RequestBody;
import java.security.MessageDigest;
import java.util.Arrays;
@RestController
public class WeChatController {
// 在微信公众平台后台配置的 Token
private final String TOKEN = "yourToken"; // 请替换成你自己的 Token
@GetMapping("/wechat")
public String validate(String signature, String timestamp, String nonce, String echostr) {
// 1. 将token, timestamp, nonce三个参数进行字典序排序
String[] arr = {TOKEN, timestamp, nonce};
Arrays.sort(arr);
// 2. 将三个参数字符串拼接成一个字符串
StringBuilder content = new StringBuilder();
for (String s : arr) {
content.append(s);
}
// 3. SHA1加密
String tmpStr = null;
try {
MessageDigest md = MessageDigest.getInstance("SHA-1");
byte[] digest = md.digest(content.toString().getBytes());
tmpStr = bytesToHex(digest);
} catch (Exception e) {
e.printStackTrace();
}
// 4. 将加密后的字符串与signature对比
if (tmpStr != null && tmpStr.equals(signature)) {
// 验证成功,原样返回 echostr
return echostr;
} else {
// 验证失败
return "error";
}
}
// 字节数组转十六进制字符串
private String bytesToHex(byte[] bytes) {
StringBuilder hexStr = new StringBuilder();
for (byte b : bytes) {
String hex = Integer.toHexString(b & 0xFF);
if (hex.length() == 1) {
hexStr.append('0');
}
hexStr.append(hex);
}
return hexStr.toString();
}
}
3 接收用户消息
当用户与你的公众号互动时,微信服务器会向你的 URL 发送一个 POST 请求,请求体是 XML 格式的消息数据。
我们需要创建一个 POST 接口来接收这些消息。
代码实现:
// WeChatController.java (续)
@PostMapping("/wechat")
public String handleWeChatMessage(@RequestBody String requestBody) {
System.out.println("收到微信消息:" + requestBody);
// 1. 解析 XML 消息
// 这里可以使用第三方库如 Dom4j 或 XStream 来简化 XML 解析
// 为了示例清晰,我们手动解析一个文本消息
try {
// ... 解析逻辑 ...
// 假设我们解析出 FromUserName, ToUserName, Content 等字段
String fromUser = "fromUser123"; // 从 XML 中解析
String toUser = "toUser456"; // 从 XML 中解析
String content = "你好"; // 从 XML 中解析
// 2. 处理消息并生成回复
String replyContent = "你发送的是: " + content;
// 3. 构造回复 XML
String replyXml = String.format(
"<xml>" +
"<ToUserName><![CDATA[%s]]></ToUserName>" +
"<FromUserName><![CDATA[%s]]></FromUserName>" +
"<CreateTime>%d</CreateTime>" +
"<MsgType><![CDATA[text]]></MsgType>" +
"<Content><![CDATA[%s]]></Content>" +
"</xml>",
fromUser, toUser, System.currentTimeMillis() / 1000, replyContent
);
return replyXml;
} catch (Exception e) {
e.printStackTrace();
return ""; // 返回空表示处理失败
}
}
提示:手动解析 XML 非常繁琐且容易出错,强烈建议使用 XStream 或 Jackson 的 XML 模块来处理,市面上也有成熟的微信 SDK,如 weixin-java-mp,它们已经封装好了这些解析逻辑。
核心功能二:被动回复与主动发送
1 被动回复用户消息
上面 handleWeChatMessage 的例子就是 被动回复,你的服务器必须在 5 秒内返回响应,否则微信会认为请求失败,被动回复是针对用户特定消息的即时回复。
2 主动发送消息(客服消息)
除了被动回复,你还可以 主动 给用户发送消息,
- 用户关注公众号后,发送欢迎语。
- 用户完成某个操作后,发送通知。
- 定期推送活动信息。
这需要使用“客服消息接口”。
步骤:
- 获取 Access Token (见下一节)。
- 构造 JSON 请求体。
- 向微信 API 发送 POST 请求。
代码示例 (假设你已经有了 accessToken):
import org.springframework.web.client.RestTemplate;
// ... 在你的 Service 类中 ...
public void sendCustomerServiceMessage(String openId, String content) {
String accessToken = getAccessToken(); // 获取 Token 的方法见下一节
String apiUrl = "https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=" + accessToken;
// 构造请求 JSON
String jsonBody = String.format(
"{\"touser\":\"%s\",\"msgtype\":\"text\",\"text\":{\"content\":\"%s\"}}",
openId, content
);
// 使用 RestTemplate 发送请求
RestTemplate restTemplate = new RestTemplate();
String response = restTemplate.postForObject(apiUrl, jsonBody, String.class);
System.out.println("主动发送消息响应: " + response);
}
核心功能三:获取 Access Token
调用微信绝大多数接口都需要一个凭证:access_token。
1 什么是 Access Token
access_token 是公众号的全局唯一接口调用凭据,有效期目前为 2小时,开发者需要进行妥善保存。
2 获取 Token 的正确姿势(缓存机制)
由于 Token 有效期只有2小时,你不能每次调用接口都去重新获取,最佳实践是:
- 本地缓存:首次请求时,从微信服务器获取 Token 并存入本地缓存(如
ConcurrentHashMap或 Redis)。 - 设置过期时间:同时设置一个比2小时稍短(如1小时50分)的过期时间。
- 检查缓存:每次需要 Token 时,先检查缓存中是否存在且未过期。
- 刷新缓存:如果缓存中没有或已过期,则重新获取并更新缓存。
代码示例 (使用 Guava Cache):
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
@Component
public class WeChatTokenService {
private final String APP_ID = "yourAppId";
private final String APP_SECRET = "yourAppSecret";
// 创建一个缓存,最大1个元素,过期时间1小时50分
private final Cache<String, String> tokenCache = CacheBuilder.newBuilder()
.maximumSize(1)
.expireAfterWrite(110, TimeUnit.MINUTES) // 1小时50分
.build();
public String getAccessToken() {
// 从缓存中获取
String token = tokenCache.getIfPresent("accessToken");
if (token == null) {
// 缓存中没有,去微信服务器获取
String apiUrl = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + APP_ID + "&secret=" + APP_SECRET;
RestTemplate restTemplate = new RestTemplate();
String response = restTemplate.getForObject(apiUrl, String.class);
// 解析 JSON 响应,提取 access_token
// 这里可以使用 Jackson 或 Gson 来解析
// 假设 response 是: {"access_token":"ACCESS_TOKEN","expires_in":7200}
// 实际项目中应使用 JSON 库
token = "从response中解析出的token";
long expiresIn = 7200; // 从response中解析出
// 放入缓存
tokenCache.put("accessToken", token);
System.out.println("Access Token 已刷新并缓存");
}
return token;
}
}
核心功能四:自定义菜单
自定义菜单可以引导用户进行特定操作。
1 菜单结构
菜单有三级结构:button -> sub_button -> sub_button,每个 button 可以有不同的类型,如 click(点击事件)、view(跳转网页)、scancode_push(扫码推事件)等。
2 创建菜单
创建菜单同样需要 access_token,并向一个特定的 API 发送一个 JSON 请求。
代码示例:
// 在 WeChatTokenService 或另一个 Service 中
public void createMenu() {
String accessToken = getAccessToken();
String apiUrl = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token=" + accessToken;
// 菜单的 JSON 结构 (根据你的需求修改)
String menuJson = "{"
+ "\"button\":["
+ " {"
+ " \"name\":\"今日歌曲\","
+ " \"type\":\"click\","
+ " \"key\":\"V1001_TODAY_MUSIC\""
+ " },"
+ " {"
+ " \"name\":\"菜单\","
+ " \"sub_button\":["
+ " {"
+ " \"type\":\"view\","
+ " \"name\":\"搜索\","
+ " \"url\":\"http://www.soso.com/\""
+ " },"
+ " {"
+ " \"type\":\"view\","
+ " \"name\":\"视频\","
+ " \"url\":\"http://v.qq.com/\""
+ " },"
+ " {"
+ " \"type\":\"click\","
+ " \"name\":\"赞一下我们\","
+ " \"key\":\"V1001_GOOD\""
+ " }"
+ " ]"
+ " }"
+ "]}";
RestTemplate restTemplate = new RestTemplate();
String response = restTemplate.postForObject(apiUrl, menuJson, String.class);
System.out.println("创建菜单响应: " + response);
}
进阶功能:网页授权获取用户信息
当用户在微信内置浏览器中访问你的网页时,你可以通过 OAuth2.0 授权获取用户的 openid 和基本信息(需用户同意)。
1 OAuth2.0 授权流程
- 引导用户授权:你的网页构造一个重定向 URL,包含
appid,redirect_uri,scope,state等参数,让用户跳转到微信的授权页面。snsapi_base: 静默授权,只获取openid。snsapi_userinfo: 弹出授权页面,可获取openid和用户基本信息(昵称、头像等)。
- 用户同意授权:用户点击同意后,微信会重定向到你的
redirect_uri,并附上code和state参数。 - 获取 access_token: 你的服务器用
code向微信服务器换取access_token和openid。 - 获取用户信息:
scope是snsapi_userinfo,则可以用上一步的access_token和openid获取用户信息。
2 实现网页授权
第一步:构造授权 URL
// 在你的 Controller 中
@GetMapping("/auth")
public void redirectToWeChatAuth(HttpServletResponse response) throws IOException {
String appId = "yourAppId";
String redirectUri = URLEncoder.encode("http://yourdomain.com/auth/callback", "UTF-8"); // 必须在公众号后台配置
String scope = "snsapi_userinfo"; // 或 snsapi_base
String state = "xyz123"; // 用于防止 CSRF 攻击,可以随机生成
String authUrl = String.format(
"https://open.weixin.qq.com/connect/oauth2/authorize?appid=%s&redirect_uri=%s&response_type=code&scope=%s&state=%s#wechat_redirect",
appId, redirectUri, scope, state
);
response.sendRedirect(authUrl);
}
第二步:处理回调
// 在你的 Controller 中
@GetMapping("/auth/callback")
public String handleAuthCallback(String code, String state) {
System.out.println("收到授权回调, code: " + code + ", state: " + state);
// 1. 用 code 换取 access_token 和 openid
String appId = "yourAppId";
String secret = "yourAppSecret";
String tokenUrl = String.format(
"https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code",
appId, secret, code
);
RestTemplate restTemplate = new RestTemplate();
String tokenResponse = restTemplate.getForObject(tokenUrl, String.class);
// 2. 解析 tokenResponse (JSON格式),获取 access_token 和 openid
// {"access_token":"ACCESS_TOKEN","expires_in":7200,"refresh_token":"REFRESH_TOKEN","openid":"OPENID","scope":"SCOPE"}
String accessToken = "从tokenResponse中解析";
String openId = "从tokenResponse中解析";
// 3. 如果需要用户信息,再次调用接口
// userInfoUrl = "https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID"
// ... 解析用户信息 ...
// 4. 业务逻辑,例如根据 openId 登录或注册你的系统
// ...
return "授权成功!";
}
总结与最佳实践
1 推荐的第三方 SDK
自己从头实现所有功能非常耗时且容易出错,强烈推荐使用成熟的第三方 SDK,它们封装了所有底层细节,让你专注于业务逻辑。
- weixin-java-mp (强烈推荐)
- 国内最流行、维护最好的微信 Java SDK。
- 支持公众号、小程序、企业微信。
- 提供了消息收发、菜单管理、用户管理、JS-SDK、网页授权等全套功能。
- 文档和示例非常丰富。
使用 weixin-java-mp 的例子:
// 引入依赖
// <dependency>
// <groupId>com.github.binarywang</groupId>
// <artifactId>weixin-java-mp</artifactId>
// <version>最新版本</version>
// </dependency>
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.api.WxMpMessageHandler;
import me.chanjar.weixin.mp.api.WxMpMessageRouter;
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
// 初始化 WxMpService
WxMpService wxMpService = ...; // 需要配置 appId, secret
// 创建消息路由器
WxMpMessageRouter router = new WxMpMessageRouter(wxMpService);
// 添加文本消息处理器
router.rule().async(false).msgType("text").handler(new WxMpMessageHandler() {
@Override
public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context) {
// wxMessage 已经被 SDK 解析好了,直接使用
return WxMpXmlOutMessage.TEXT().content("你发送的是: " + wxMessage.getContent())
.fromUser(wxMessage.getToUser()).toUser(wxMessage.getFromUser()).build();
}
}).next();
// 在你的 Controller 中使用
@PostMapping("/wechat")
public String handleWeChatPost(@RequestBody String requestBody, @RequestParam("signature") String signature,
@RequestParam("timestamp") String timestamp, @RequestParam("nonce") String nonce,
@RequestParam("openid") String openid) {
// 使用路由器处理消息
return router.route(requestBody, signature, timestamp, nonce, openid);
}
可以看到,使用 SDK 后,代码量大大减少,逻辑也变得非常清晰。
2 消息加解密
如果你的公众号开启了“安全模式”或“兼容模式”,所有收发消息都需要进行 AES 加密解密,SDK 已经提供了完美的支持,你只需要在初始化 WxMpService 时配置好 EncodingAESKey 即可。
3 异步处理
对于一些耗时的操作(如调用外部 API、数据库操作),建议使用 异步处理(如 Spring 的 @Async),避免在 5 秒的被动回复超时时间内做太多事情,导致用户体验下降。
4 部署上线
- 公网 IP: 你的服务器必须有公网 IP。
- 域名: 建议使用备案的域名,并将域名解析到你的服务器 IP。
- HTTPS: 微信的所有接口都要求使用
https协议,你需要为你的域名申请 SSL 证书(如 Let's Encrypt 免费证书)并配置到你的 Web 服务器(如 Nginx)上。 - 反向代理: 生产环境中,通常使用 Nginx 作为反向代理,处理 HTTPS 请求并转发到内部的 Spring Boot 应用。
希望这份详细的教程能帮助你顺利开启微信公众号的 Java 开发之旅!祝你编码愉快!
