杰瑞科技汇

微信Java开发教程,如何快速上手?

微信平台开发教程 (Java 版)

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

微信Java开发教程,如何快速上手?-图1
(图片来源网络,侵删)
  1. 准备工作:注册公众号、获取必要信息。
  2. 环境搭建:创建 Java Web 项目。
  3. 核心概念:理解微信服务器与开发者服务器的交互模式。
  4. 接入验证:实现微信服务器的首次校验。
  5. 接收和发送消息:处理用户的各种输入(文本、图片、菜单点击等)并回复。
  6. 高级功能:如获取用户信息、网页授权等。
  7. 推荐工具与框架:提升开发效率。

第一章:准备工作

在开始编码之前,你必须完成以下准备工作:

1 注册微信公众号

你需要一个公众号来进行开发,主要分为两种:

  • 订阅号:适合媒体和个人,主要功能是信息推送,认证后可以获取高级接口权限。
  • 服务号:适合企业和组织,功能更强大,如微信支付、用户管理等。

对于初学者和学习目的,订阅号完全足够。

注册地址https://mp.weixin.qq.com/

微信Java开发教程,如何快速上手?-图2
(图片来源网络,侵删)

按照官网指引完成注册和认证(认证需要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>

第三章:核心概念 - 消息交互流程

理解这个流程是开发的关键。

  1. 用户 在公众号里发送消息或点击菜单。
  2. 微信服务器 接收到消息后,将其封装成 XML 格式,POST 请求到你之前在后台配置的 URL
  3. 你的服务器 (Java Web App) 接收到这个 POST 请求。
  4. 你的服务器 解析 XML,根据消息类型(文本、图片、事件等)进行业务处理。
  5. 你的服务器 将要回复的内容也按照特定的 XML 格式组装好。
  6. 你的服务器 将这个 XML 响应体作为 HTTP Response 返回给 微信服务器
  7. 微信服务器 再将这个回复内容推送给 用户

第四章:接入验证 (实现 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 部署并测试

  1. 将你的项目打包成 WAR 文件并部署到你的服务器上。
  2. 确保 http://yourdomain.com/wechat 可以访问到 WeChatServlet
  3. 回到微信后台,点击 “提交”,如果配置正确,页面会提示“成功”。

第五章:接收和发送消息 (实现 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 解析库,dom4jJAXB,这里我们用 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,你的代码会变得非常简单:

  1. 添加依赖

    <dependency>
        <groupId>cn.binarywang</groupId>
        <artifactId>weixin-java-mp</artifactId>
        <version>4.4.0</version> <!-- 请使用最新版本 -->
    </dependency>
  2. 配置 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;
        }
    }
  3. 创建 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 开发微信公众号后端的全过程:

  1. 从基础配置 开始,理解了微信服务器与开发者服务器的交互模式。
  2. 通过 Servlet 实现了最核心的接入验证和消息收发,让你对底层原理有了深刻理解。
  3. 介绍了 Access Token 和获取用户信息等高级功能
  4. 强烈推荐了 WxJava 这样的优秀 SDK,并展示了如何使用它来简化开发。

对于任何严肃的项目,都请直接使用 WxJava,它将为你节省大量的时间和精力,并避免很多潜在的坑,而对于初学者,亲手实现一遍基础功能是非常有价值的,祝开发顺利!

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