杰瑞科技汇

Java微信公众号开发从哪开始学?

目录

  1. 准备工作
    • 1 注册微信公众号
    • 2 获取开发者凭证
    • 3 选择开发模式
    • 4 本地开发与外网访问
  2. 核心技术栈
    • 1 Spring Boot (推荐)
    • 2 Servlet API
    • 3 HTTP 客户端
  3. 第一步:接入微信公众平台(验证服务器地址有效性)
    • 1 原理讲解
    • 2 代码实现
    • 3 部署与测试
  4. 第二步:接收和回复用户消息
    • 1 消息类型介绍
    • 2 接收消息流程
    • 3 解析 XML 消息
    • 4 构造回复消息
    • 5 完整代码示例(文本消息)
  5. 第三步:开发自定义菜单
    • 1 获取 Access Token
    • 2 创建菜单
    • 3 代码实现
  6. 进阶与总结
    • 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 上进行开发,你需要:

  • 使用内网穿透工具(推荐新手)
    • 下载并安装工具,如 ngrokfrp 等。
    • 运行命令,ngrok http 8080,它会生成一个公网域名,如 https://xxxxxx.ngrok.io
    • 将这个公网域名填入公众号后台的 URL 中,端口填你应用的端口(如 8080)。
  • 购买云服务器

    在阿里云、腾讯云等平台购买一台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 的类,并重写 doGetdoPost 方法。

3 HTTP 客户端

在调用微信 API(如创建菜单)时,需要发送 HTTP 请求,推荐使用成熟的库:

  • OkHttp: 轻量级,性能好。
  • Apache HttpClient: 功能强大,稳定。
  • Spring RestTemplate: 如果是 Spring Boot 项目,自带且使用方便。

第一步:接入微信公众平台(验证服务器地址有效性)

这是开发的第一步,目的是让微信服务器确认你的服务器是“你自己的”。

1 原理讲解

当你在公众号后台提交 URL 和 Token 后,微信服务器会向你的 URL 发送一个 GET 请求,请求中包含四个参数:

  • signature: 微信加密签名
  • timestamp: 时间戳
  • nonce: 随机数
  • echostr: 随机字符串

你的服务器需要做以下两件事来验证:

  1. 验证签名:将 tokentimestampnonce 三个参数进行字典序排序,然后拼接成一个字符串,进行 SHA1 加密,将加密后的字符串与 signature 对比,如果一致,则请求来自微信。
  2. 返回 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 部署与测试

  1. 启动你的 Spring Boot 应用。
  2. 确保内网穿透工具或云服务器配置正确,使得 https://yourdomain.com/wechat 可以访问。
  3. 在公众号后台的 基本配置 页面,填写 URL 和 Token,点击 “提交”
  4. 如果提示“成功”,则表示服务器接入验证完成!

第二步:接收和回复用户消息

接入成功后,用户在公众号里发送的任何消息,微信服务器都会通过你配置的 URL 以 POST 请求的形式推送到你的服务器。

1 消息类型介绍

用户发送的消息和公众号回复的消息都有多种格式,最常见的是 文本消息

  • 用户发送的消息类型:
    • text: 文本消息
    • image: 图片消息
    • voice: 语音消息
    • video: 视频消息
    • shortvideo: 小视频消息
    • location: 地理位置消息
    • link: 链接消息
  • 公众号回复的消息类型:
    • text: 文本回复
    • image: 图片回复
    • voice: 语音回复
    • video: 视频回复
    • music: 音乐回复
    • news: 图文消息回复(多条)

2 接收消息流程

  1. 用户在公众号输入并发送消息。
  2. 微信服务器将消息打包成 XML 格式,通过 POST 请求发送到你的服务器 URL。
  3. 你的服务器解析 XML,并根据消息内容进行处理。
  4. 你的服务器在 5 秒内,将回复内容也打包成 XML 格式,通过 HTTP 响应返回给微信服务器。
  5. 微信服务器再将你的回复推送给用户。

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>

注意:ToUserNameFromUserName 的值与接收到的消息正好相反。

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 映射或使用 dom4jJAXB 默认不会处理 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 消息加解密(安全模式)

当公众号后台配置为“安全模式”时,微信发送过来的消息是经过加密的,你需要:

  1. 下载微信官方提供的 加解密库(如 wxBotWxJava 项目中的相关模块)。
  2. 在你的服务器上保存 EncodingAESKey(在公众号后台获取)。
  3. 使用库提供的工具类来解密收到的消息,并在回复时加密。

2 常用 API 调用

除了创建菜单,微信还提供了丰富的 API,

  • 用户管理: 获取用户信息、关注/取消关注事件等。
  • 素材管理: 上传和获取图片、语音等素材。
  • 模板消息: 发送服务通知,如订单确认、物流更新等。
  • 客服消息: 7天内,可以给用户发特定类型的消息。

调用这些 API 的流程都类似:获取 access_token -> 拼接 API URL -> 发送 HTTP 请求(GET/POST)-> 处理返回结果。

3 推荐学习资源

  • 微信官方文档: 最权威、最全面的资料,所有开发细节都在这里。
  • WxJava 开源项目: 一个功能非常强大的 Java 微信开发 SDK,封装了几乎所有微信 API,能极大简化你的开发工作,如果你觉得从零开始写很麻烦,直接使用它是最好的选择。
  • 微信 JS-SDK: 如果你的公众号需要嵌入 H5 页面,并调用微信的分享、拍照、扫一扫等功能,就需要用到 JS-SDK。

Java 微信公众号开发的核心在于:

  1. 理解微信的通信机制(验证、消息推送、API调用)。
  2. 搭建一个能接收和响应 HTTP 请求的 Web 服务(Spring Boot 是最佳选择)。
  3. 熟练处理 XML 和 JSON 数据的解析与构造。
  4. 安全地管理你的 AppSecret,并正确获取和使用 access_token

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

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