杰瑞科技汇

微信公众平台Java开发怎么入门?

微信公众平台Java开发完整教程

本教程将引导你使用 Java 语言开发一个微信公众号的后端服务,我们将使用流行的 Spring Boot 框架来简化开发过程。

微信公众平台Java开发怎么入门?-图1
(图片来源网络,侵删)

目录

  1. 前置准备:理解微信公众平台类型
  2. 第一步:准备工作(服务器与域名)
  3. 第二步:创建公众号并获取必要信息
  4. 第三步:Java后端项目搭建(Spring Boot)
  5. 第四步:实现服务器配置(Token验证)
  6. 第五步:核心功能:接收与回复用户消息
    • 1 接收文本消息
    • 2 回复文本消息
    • 3 处理其他类型消息(关注、点击菜单等)
  7. 第六步:高级API开发
    • 1 网页授权获取用户信息
    • 2 发送模板消息
  8. 第七步:部署上线
  9. 总结与进阶

前置准备:理解微信公众平台类型

微信公众平台主要分为三种账号,它们的开发权限和能力差异巨大,请务必先确认你要开发的是哪一种:

  • 订阅号

    • 功能:主要为用户提供信息和资讯,每天可以推送一次消息。
    • 开发权限:有基本的自定义菜单、自动回复等开发接口,但没有微信支付、高级接口(如用户信息获取、模板消息)等核心能力。
    • 适用场景:媒体、个人博主、内容创作者。
  • 服务号

    • 功能:主要为用户提供服务和服务通知,每月可以推送四次消息(重要服务通知会及时送达)。
    • 开发权限:拥有所有高级接口,包括微信支付、用户信息获取、模板消息、JS-SDK等,这是企业级应用开发的首选。
    • 适用场景:企业、政府、银行等需要提供服务或电商交易的组织。
  • 小程序

    微信公众平台Java开发怎么入门?-图2
    (图片来源网络,侵删)
    • 功能:一种不需要下载安装即可使用的应用,用完即走。
    • 开发:开发方式与公众号不同,有自己的开发者工具和API体系,本教程不涉及小程序开发。

如果你的目标是开发一个能提供完整服务(如会员、电商、通知)的应用,你需要一个服务号


第一步:准备工作(服务器与域名)

在开始编码前,你需要准备一个可以公网访问的服务器和一个备案的域名。

  • 服务器:推荐使用云服务器,如阿里云、腾讯云、华为云等,系统可以是 Linux (如 CentOS, Ubuntu) 或 Windows Server,但 Linux 更为常见和推荐。
  • 域名:需要备案(如果服务器在国内),在公众号后台配置时,需要使用这个域名。

第二步:创建公众号并获取必要信息

  1. 访问 微信公众平台官网,使用你的邮箱注册一个服务号(或订阅号)。
  2. 登录后台,在 “开发” -> “基本配置” 中,你会看到服务器配置信息。

你需要获取以下关键信息:

  • AppID (开发者ID):公众号的唯一标识。
  • AppSecret (开发者密码):与AppID配对,用于调用高级API,请务必保密
  • 服务器配置(URL, Token, EncodingAESKey)
    • URL:你的Java后端服务器的公网访问地址,必须以 http://https:// 开头,并且支持80端口(或443端口)的80-bit消息体验证。
    • Token:可以任意填写,用作开发者服务器与微信服务器之间的验证令牌。
    • EncodingAESKey:用于消息加解密,可以随机生成,如果你的公众号属于“企业号”或“服务号”,建议启用加密模式。

第三步:Java后端项目搭建(Spring Boot)

我们将使用 Spring Boot 快速搭建一个Web项目。

微信公众平台Java开发怎么入门?-图3
(图片来源网络,侵删)
  1. 使用 Spring Initializr 创建项目

    • 访问 start.spring.io
    • 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 或更高版本
    • Dependencies: 添加 Spring Web 依赖。
    • 点击 "GENERATE" 下载项目压缩包,并用你的IDE(如 IntelliJ IDEA 或 Eclipse)打开。
  2. 项目结构 你的项目会自动生成一个主启动类 WechatDemoApplication.java


第四步:实现服务器配置(Token验证)

这是接入微信开发的第一步,也是最关键的一步,微信服务器会向你的 URL 发送一个 GET 请求,以验证你的服务器是合法的。

  1. 编写Controllersrc/main/java/com/example/wechatdemo 目录下,创建一个新的 Controller 类 WeChatController.java

    package com.example.wechatdemo;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.RestController;
    import java.security.MessageDigest;
    import java.security.NoSuchAlgorithmException;
    import java.util.Arrays;
    @RestController
    @RequestMapping("/wechat")
    public class WeChatController {
        // 在微信公众平台后台填写的Token
        private static final String TOKEN = "your_token";
        @GetMapping
        public String verify(@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 = "";
            try {
                MessageDigest md = MessageDigest.getInstance("SHA-1");
                byte[] digest = md.digest(content.toString().getBytes());
                tmpStr = byteToHex(digest);
            } catch (NoSuchAlgorithmException e) {
                e.printStackTrace();
            }
            // 3. 将sha1加密后的字符串可与signature对比
            if (tmpStr != null && tmpStr.equals(signature)) {
                // 请求来自微信,原样返回echostr参数内容
                return echostr;
            } else {
                // 请求来自非微信,验证失败
                return "error";
            }
        }
        /**
         * 字节数组转十六进制字符串
         */
        private static String byteToHex(byte[] bytes) {
            char[] hexDigits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
            char[] chars = new char[bytes.length * 2];
            int k = 0;
            for (byte b : bytes) {
                chars[k++] = hexDigits[b >>> 4 & 0xf];
                chars[k++] = hexDigits[b & 0xf];
            }
            return new String(chars);
        }
    }
  2. 配置运行参数

    • 如果你使用 IDE:在 Run/Debug Configuration 中,为 WechatDemoApplication 设置以下 VM options:

      -Dserver.port=80

      微信服务器配置要求使用80端口。

    • 如果你使用打包部署:在 application.propertiesapplication.yml 中配置端口为80。

      server.port=80
  3. 启动项目并进行验证

    • 运行 WechatDemoApplication
    • 在你的浏览器中访问 http://你的服务器IP地址/wechat,应该会看到错误信息 "error",这是正常的,因为缺少了微信验证所需的参数。
    • 回到微信公众平台后台,填写服务器配置信息:
      • URL: http://你的域名或IP/wechat (http://123.456.789.101/wechat)
      • Token: your_token (与代码中填写的完全一致)
      • EncodingAESKey: 随机生成或填写
    • 点击 “提交”,如果一切配置正确,会提示“成功”。

第五步:核心功能:接收与回复用户消息

当用户与你的公众号互动时(如发送文本、点击菜单、关注公众号),微信服务器会向你的 URL 发送一个 POST 请求,我们需要解析这个请求,并根据业务逻辑回复消息。

  1. 消息接收与回复原理

    • 接收:微信服务器将用户消息打包成 XML 格式,通过 POST 请求发送到你的服务器。
    • 回复:你的服务器处理完消息后,需要返回一个同样格式的 XML 响应给微信服务器,微信服务器再将这个响应转发给用户。
  2. 添加依赖 为了方便解析和生成XML,我们添加 Jackson 依赖(Spring Boot Web 默认包含)和 dom4j

    pom.xml 中添加:

    <dependency>
        <groupId>org.dom4j</groupId>
        <artifactId>dom4j</artifactId>
        <version>2.1.3</version>
    </dependency>
  3. 创建消息处理类 创建一个 MessageHandler 类来专门处理接收到的消息。

    package com.example.wechatdemo;
    import org.dom4j.Document;
    import org.dom4j.DocumentException;
    import org.dom4j.Element;
    import org.dom4j.io.SAXReader;
    import org.springframework.stereotype.Component;
    import javax.servlet.http.HttpServletRequest;
    import java.io.InputStream;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    @Component
    public class MessageHandler {
        public String handle(HttpServletRequest request) {
            try (InputStream is = request.getInputStream()) {
                SAXReader reader = new SAXReader();
                Document document = reader.read(is);
                Element root = document.getRootElement();
                // 获取消息类型
                String msgType = root.element("MsgType").getTextTrim();
                switch (msgType) {
                    case "text":
                        return handleTextMessage(root);
                    case "event":
                        return handleEventMessage(root);
                    default:
                        // 其他类型的消息暂不处理
                        return "";
                }
            } catch (Exception e) {
                e.printStackTrace();
                return "";
            }
        }
        private String handleTextMessage(Element root) {
            // 获取用户发送的文本内容
            String content = root.element("Content").getTextTrim();
            // 获取发送者OpenID
            String fromUser = root.element("FromUserName").getTextTrim();
            // 获取接收者公众号的OpenID
            String toUser = root.element("ToUserName").getTextTrim();
            // 构造回复文本消息
            String replyContent = "你发送的是: " + content;
            return buildTextMessage(toUser, fromUser, replyContent);
        }
        private String handleEventMessage(Element root) {
            String eventType = root.element("Event").getTextTrim();
            String fromUser = root.element("FromUserName").getTextTrim();
            String toUser = root.element("ToUserName").getTextTrim();
            if ("subscribe".equals(eventType)) {
                // 用户关注事件
                return buildTextMessage(toUser, fromUser, "感谢关注!欢迎来到我们的公众号!");
            } else if ("unsubscribe".equals(eventType)) {
                // 用户取消关注事件
                // 这里可以做一些清理工作,比如从数据库删除用户信息
                System.out.println("用户 " + fromUser + " 取消关注了");
            }
            // 其他事件(如点击菜单CLICK)可以在这里处理
            return "";
        }
        /**
         * 构造回复文本消息的XML
         */
        private String buildTextMessage(String toUser, String fromUser, String content) {
            return "<xml>" +
                    "<ToUserName><![CDATA[" + toUser + "]]></ToUserName>" +
                    "<FromUserName><![CDATA[" + fromUser + "]]></FromUserName>" +
                    "<CreateTime>" + System.currentTimeMillis() / 1000 + "</CreateTime>" +
                    "<MsgType><![CDATA[text]]></MsgType>" +
                    "<Content><![CDATA[" + content + "]]></Content>" +
                    "</xml>";
        }
    }
  4. 修改Controller以处理POST请求 修改 WeChatController,增加一个 POST 方法来处理消息。

    // ... (之前的GET方法保持不变)
    import org.springframework.web.bind.annotation.PostMapping;
    // ... 其他import
    @RestController
    @RequestMapping("/wechat")
    public class WeChatController {
        // ... TOKEN常量
        @Autowired
        private MessageHandler messageHandler;
        // ... GET方法用于验证
        @PostMapping
        public String handlePost(HttpServletRequest request) {
            // 将消息处理逻辑委托给MessageHandler
            return messageHandler.handle(request);
        }
    }
  5. 测试

    • 重新启动你的Spring Boot应用。
    • 用你的个人微信关注这个公众号。
    • 给公众号发送任意文本消息,你应该会收到 "你发送的是: [你发送的内容]" 的回复。
    • 取消关注后再关注,应该会收到 "感谢关注!" 的消息。

第六步:高级API开发

现在你已经掌握了基础消息交互,让我们来调用一些高级API,这需要用到之前获取的 AppIDAppSecret

1 网页授权获取用户信息

当用户通过公众号菜单或消息中的链接访问你的网页时,你可以通过网页授权获取用户的 OpenID 和基本信息。

  1. 实现一个获取Access Token的工具类

    package com.example.wechatdemo.utils;
    import com.fasterxml.jackson.databind.JsonNode;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import org.springframework.stereotype.Component;
    import java.io.IOException;
    import java.net.URI;
    import java.net.http.HttpClient;
    import java.net.http.HttpRequest;
    import java.net.http.HttpResponse;
    @Component
    public class WeChatApiUtil {
        private static final String APPID = "your_appid";
        private static final String APPSECRET = "your_appsecret";
        private static final String GET_ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + APPID + "&secret=" + APPSECRET;
        private static final String GET_USERINFO_URL = "https://api.weixin.qq.com/cgi-bin/user/info?access_token=%s&openid=%s&lang=zh_CN";
        // 获取Access Token (注意:有2小时有效期,建议缓存)
        public String getAccessToken() throws IOException, InterruptedException {
            HttpClient client = HttpClient.newHttpClient();
            HttpRequest request = HttpRequest.newBuilder()
                    .uri(URI.create(GET_ACCESS_TOKEN_URL))
                    .build();
            HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
            ObjectMapper mapper = new ObjectMapper();
            JsonNode node = mapper.readTree(response.body());
            return node.get("access_token").asText();
        }
        // 通过网页授权Code获取OpenID和Access Token
        public JsonNode getSnsAccessToken(String code) throws IOException, InterruptedException {
            String url = String.format("https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code", APPID, APPSECRET, code);
            HttpClient client = HttpClient.newHttpClient();
            HttpRequest request = HttpRequest.newBuilder().uri(URI.create(url)).build();
            HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
            return new ObjectMapper().readTree(response.body());
        }
        // 获取用户信息
        public JsonNode getUserInfo(String openId, String accessToken) throws IOException, InterruptedException {
            String url = String.format(GET_USERINFO_URL, accessToken, openId);
            HttpClient client = HttpClient.newHttpClient();
            HttpRequest request = HttpRequest.newBuilder().uri(URI.create(url)).build();
            HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
            return new ObjectMapper().readTree(response.body());
        }
    }
  2. 配置网页授权

    • 在微信公众平台后台,进入 “开发” -> “接口权限” -> “网页授权获取用户基本信息”
    • 点击 “修改”,将你的回调域名填入,你的网页地址是 http://yourdomain.com/callback,那么就填 yourdomain.com (不要带协议和路径)。
  3. 实现OAuth2授权流程

    • 在你的Controller中添加一个方法来处理回调。
    // 在 WeChatController 中添加
    @Autowired
    private WeChatApiUtil weChatApiUtil;
    @GetMapping("/oauth/callback")
    public String oauthCallback(@RequestParam("code") String code) throws IOException, InterruptedException {
        // 1. 通过code获取access_token和openid
        JsonNode snsTokenNode = weChatApiUtil.getSnsAccessToken(code);
        String openId = snsTokenNode.get("openid").asText();
        String accessToken = snsTokenNode.get("access_token").asText();
        // 2. 使用access_token和openid获取用户信息
        JsonNode userInfoNode = weChatApiUtil.getUserInfo(openId, accessToken);
        String nickname = userInfoNode.get("nickname").asText();
        String headImgUrl = userInfoNode.get("headimgurl").asText();
        // 3. 在这里可以将用户信息存入数据库或进行其他业务处理
        System.out.println("获取到用户信息: " + userInfoNode.toString());
        return "欢迎, " + nickname + "!";
    }
  4. 生成授权链接 当用户需要授权时,构造如下链接并发送给用户(通常在菜单项或H5页面中):

    https://open.weixin.qq.com/connect/oauth2/authorize?appid=YOUR_APPID&redirect_uri=YOUR_REDIRECT_URI&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect
    • YOUR_APPID: 你的AppID
    • YOUR_REDIRECT_URI: 你的回调地址,如 http://yourdomain.com/wechat/oauth/callback (必须与后台配置的域名一致)
    • scope: snsapi_userinfo 表示弹出授权页面,获取用户信息;snsapi_base 表示静默授权,只获取OpenID。

2 发送模板消息

模板消息用于向用户发送重要的服务通知,如订单确认、物流更新等。

  1. 配置模板

    • 在微信公众平台后台,进入 “模板消息”,选择一个合适的行业模板,并添加到你的账号中,你会得到一个 模板ID
  2. 实现发送模板消息的工具方法WeChatApiUtil 中添加一个方法:

    // 在 WeChatApiUtil 中添加
    private static final String SEND_TEMPLATE_URL = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=%s";
    public void sendTemplateMessage(String accessToken, String openId, String templateId, String url, Map<String, TemplateData> data) throws IOException, InterruptedException {
        // 构造请求体JSON
        Map<String, Object> requestBody = new HashMap<>();
        requestBody.put("touser", openId);
        requestBody.put("template_id", templateId);
        requestBody.put("url", url);
        requestBody.put("data", data);
        ObjectMapper mapper = new ObjectMapper();
        String jsonBody = mapper.writeValueAsString(requestBody);
        HttpClient client = HttpClient.newHttpClient();
        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(String.format(SEND_TEMPLATE_URL, accessToken)))
                .header("Content-Type", "application/json")
                .POST(HttpRequest.BodyPublishers.ofString(jsonBody))
                .build();
        HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
        System.out.println("发送模板消息响应: " + response.body());
    }
    // 定义一个内部类来表示模板数据项
    public static class TemplateData {
        private String value;
        private String color;
        public TemplateData(String value, String color) {
            this.value = value;
            this.color = color;
        }
        // Getters and Setters
        public String getValue() { return value; }
        public void setValue(String value) { this.value = value; }
        public String getColor() { return color; }
        public void setColor(String color) { this.color = color; }
    }
  3. 调用发送方法 在你的业务逻辑中(比如用户下单后),调用这个方法:

    // 在某个Service或Controller中
    @Autowired
    private WeChatApiUtil weChatApiUtil;
    public void onOrderPlaced(String openId) {
        try {
            String accessToken = weChatApiUtil.getAccessToken(); // 获取全局access_token
            String templateId = "your_template_id"; // 你的模板ID
            String url = "http://yourdomain.com/order/123"; // 点击消息后跳转的链接
            Map<String, TemplateData> data = new HashMap<>();
            data.put("first", new TemplateData("您有新的订单待处理!", "#173177"));
            data.put("keyword1", new TemplateData("202510270001", "#173177"));
            data.put("keyword2", new TemplateData("¥99.00", "#173177"));
            data.put("remark", new TemplateData("请尽快处理,谢谢!", "#173177"));
            weChatApiUtil.sendTemplateMessage(accessToken, openId, templateId, url, data);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

第七步:部署上线

开发完成后,你需要将你的应用部署到公网服务器上。

  1. 打包项目:在项目根目录执行 mvn clean package,会生成一个 .jar 文件。

  2. 上传JAR包:将生成的 .jar 文件上传到你的云服务器。

  3. 运行应用

    • 确保服务器已安装 JDK。
    • 使用 nohup 命令让程序在后台运行,并将日志输出到文件:
      nohup java -jar wechat-demo.jar > wechat.log 2>&1 &
  4. 配置Nginx (可选但推荐)

    • 使用 Nginx 作为反向代理,可以处理80/443端口,并提供HTTPS(需要SSL证书)。

    • Nginx 配置示例:

      server {
          listen 80;
          server_name yourdomain.com;
          location /wechat {
              proxy_pass http://127.0.0.1:80; # 假设你的Spring Boot应用监听在80端口
              proxy_set_header Host $host;
              proxy_set_header X-Real-IP $remote_addr;
              proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
          }
      }

总结与进阶

恭喜!你已经完成了微信公众平台Java开发的核心部分。

  • 我们学习了如何搭建Spring Boot项目、如何接入微信服务器、如何处理用户消息,以及如何调用网页授权和模板消息等高级API。
  • 进阶方向
    • 微信支付:集成微信支付,实现扫码支付、JSAPI支付等。
    • JS-SDK:在网页中调用微信原生能力,如拍照、选图、分享、地理位置等。
    • 多公众号管理:设计一套系统来管理多个公众号。
    • 更完善的架构:使用消息队列(如RabbitMQ)来解耦消息处理,使用缓存(如Redis)来存储Access Token。
    • 代码优化:使用更优雅的XML处理库(如JAXB),或者将消息处理逻辑模块化。

这份教程为你打下了坚实的基础,希望你在微信开发的路上越走越远!

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