杰瑞科技汇

Java API如何对接微信公众平台?

微信公众平台为开发者提供了丰富的接口,用于与公众号进行交互,在 Java 后端开发中,我们通常不会直接去实现底层的 HTTP 请求和签名验证,而是会使用成熟的第三方库来简化开发。

Java API如何对接微信公众平台?-图1
(图片来源网络,侵删)

下面我将从 核心概念、主流库选择、功能模块实现、开发流程 四个方面进行详细说明。


核心概念

在开始编码前,必须理解微信平台的几个核心概念:

  1. AppID 与 AppSecret

    • 公众号的唯一标识,在微信公众平台后台获取。
    • 用于获取 access_token,调用绝大多数接口的凭证。
  2. Access Token

    Java API如何对接微信公众平台?-图2
    (图片来源网络,侵删)
    • 调用接口的全局唯一凭据,有效期为2小时(7200秒)。
    • 必须开发者自己缓存,并定时刷新(例如在过期前5分钟刷新),绝对不能每次调用接口都去重新获取,否则会触发频率限制。
    • 缓存方案:可以使用 Redis、Guava Cache、或者数据库。
  3. JSAPI Ticket

    • 用于生成 JS-SDK 权限签名,有效期为2小时(7200秒)。
    • 它的获取也需要 access_token,所以通常和 access_token 一起进行缓存管理。
  4. 开发者模式(服务器配置)

    • 这是你的 Java 后台服务器与微信服务器通信的桥梁。
    • 你需要配置一个 URL(你的服务器地址)和 Token(一个自定义字符串)。
    • 微信服务器会向你的 URL 发送一个 GET 请求,验证你服务器的所有权,你需要按照微信的规则进行签名并返回 echostr
  5. 消息加解密

    • 在安全模式下,微信服务器与你的服务器之间的所有消息(包括事件推送和用户消息)都是加密的。
    • 你需要使用微信提供的 AES 加密算法进行解密,以及将返回的消息进行加密再发送给微信。
    • 好消息是:主流的 Java 库都封装了这部分逻辑,你无需关心底层实现。

主流 Java 库选择

选择一个合适的库是成功的一半,以下是几个非常优秀且广泛使用的 Java 微信 SDK:

Java API如何对接微信公众平台?-图3
(图片来源网络,侵删)
库名称 GitHub 链接 特点 推荐场景
WxJava https://github.com/Wechat-Group/WxJava 功能最全、社区最活跃、文档最完善、维护最积极,覆盖了公众号、小程序、企业微信等几乎所有微信生态。 强烈推荐,无论是个人学习还是企业级项目,都是首选。
JeecgBoot https://github.com/jeecg/jeecg-boot 一个基于 Spring Boot + Mybatis-plus 的低代码开发平台,其核心模块中集成了微信功能。 如果你的项目本身是基于 JeecgBoot 的,可以直接使用其内置模块。
其他小众库 (略) 功能相对单一,可能不再维护。 不推荐用于新项目。

对于绝大多数 Java 开发者,直接使用 WxJava 是最佳选择。 本文后续内容将主要围绕 WxJava 进行讲解。


功能模块实现(以 WxJava 为例)

项目集成

pom.xml 中添加 WxJava 依赖(请根据需要选择模块,wx-java-mp 代表公众号):

<dependency>
    <groupId>cn.binarywang</groupId>
    <artifactId>wx-java-mp-spring-boot-starter</artifactId>
    <version>最新版本号</version> <!-- 请务必去 GitHub 查找最新版本 -->
</dependency>

配置文件

application.ymlapplication.properties 中配置你的公众号信息:

wx:
  mp:
    configs:
      - appId: "你的AppID"
        secret: "你的AppSecret"
        token: "你在开发者模式配置的Token"
        aesKey: "在高级功能->开发者模式中配置的EncodingAESKey (可选,安全模式需要)"
        # 如果有多个公众号,可以配置多个

核心功能示例

A. 接收和回复文本消息

你需要创建一个 Controller 来处理来自微信服务器的请求。

import me.chanjar.weixin.mp.api.WxMpMessageHandler;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/wx")
public class WxController {
    @Autowired
    private WxMpService wxMpService; // WxJava 会自动注入配置好的 Service
    /**
     * 处理微信服务器发来的消息和事件
     * @param signature 微信加密签名
     * @param timestamp 时间戳
     * @param nonce 随机数
     * @param encryptType 加密方式 (如 aes)
     * @param msgSignature 消息签名
     * @param requestBody 请求体
     */
    @PostMapping(value = "/mp/{yourAppId}", produces = "application/xml;charset=UTF-8")
    public String handleMsg(
            @PathVariable String yourAppId,
            @RequestParam("signature") String signature,
            @RequestParam("timestamp") String timestamp,
            @RequestParam("nonce") String nonce,
            @RequestParam(name = "encrypt_type", required = false) String encryptType,
            @RequestParam("msg_signature") String msgSignature,
            @RequestBody String requestBody) {
        // WxJava 会自动处理签名验证和消息解密
        WxMpXmlMessage inMessage = WxMpXmlMessage.fromEncryptedXml(
                requestBody, wxMpService.getWxMpConfigStorage(yourAppId), timestamp, nonce, msgSignature);
        // 构造回复消息
        WxMpXmlOutMessage outMessage = this.route(inMessage);
        // 如果是加密模式,WxJava 会自动加密回复消息
        if (outMessage.getEncrypt() != null) {
            return outMessage.toEncryptedXml(wxMpService.getWxMpConfigStorage(yourAppId));
        }
        return outMessage.toXml();
    }
    private WxMpXmlOutMessage route(WxMpXmlMessage inMessage) {
        // 根据消息类型进行路由处理
        switch (inMessage.getMsgType()) {
            case TEXT:
                // 处理文本消息
                return handleTextMessage(inMessage);
            case EVENT:
                // 处理事件推送 (如关注、点击菜单等)
                return handleEventMessage(inMessage);
            default:
                return null;
        }
    }
    private WxMpXmlOutMessage handleTextMessage(WxMpXmlMessage inMessage) {
        // 用户发送的内容
        String content = inMessage.getContent();
        // 简单的自动回复
        String replyContent = "你发送了文本消息: " + content;
        // 创建一个文本回复消息
        return WxMpXmlOutMessage.TEXT()
                .content(replyContent)
                .fromUser(inMessage.getToUser())
                .toUser(inMessage.getFromUser())
                .build();
    }
    private WxMpXmlOutMessage handleEventMessage(WxMpXmlMessage inMessage) {
        String eventType = inMessage.getEvent();
        if ("subscribe".equals(eventType)) {
            // 用户关注事件
            return WxMpXmlOutMessage.TEXT()
                    .content("感谢关注!")
                    .fromUser(inMessage.getToUser())
                    .toUser(inMessage.getFromUser())
                    .build();
        }
        // ... 其他事件处理
        return null;
    }
}

B. 获取 Access Token

WxJava 已经帮你封装好了获取和缓存的逻辑,你只需要注入 WxMpService 并调用即可。

@RestController
public class TokenController {
    @Autowired
    private WxMpService wxMpService;
    @GetMapping("/getAccessToken")
    public String getAccessToken() {
        // getAccessToken() 方法内部会自动处理缓存和刷新
        return wxMpService.getAccessToken();
    }
}

C. 菜单管理

创建菜单、查询菜单、删除菜单都非常简单。

@RestController
@RequestMapping("/menu")
public class MenuController {
    @Autowired
    private WxMpService wxMpService;
    /**
     * 创建自定义菜单
     */
    @PostMapping("/create")
    public String createMenu(@RequestBody WxMenu menu) {
        try {
            wxMpService.getMenuService().menuCreate(menu);
            return "菜单创建成功";
        } catch (WxErrorException e) {
            return "菜单创建失败: " + e.getError().getErrorCode() + " - " + e.getError().getErrorMsg();
        }
    }
    /**
     * 删除自定义菜单
     */
    @GetMapping("/delete")
    public String deleteMenu() {
        try {
            wxMpService.getMenuService().menuDelete();
            return "菜单删除成功";
        } catch (WxErrorException e) {
            return "菜单删除失败: " + e.getError().getErrorCode() + " - " + e.getError().getErrorMsg();
        }
    }
}

D. 网页授权(获取用户 OpenID)

当用户在微信内点击你的链接时,可以通过网页授权获取用户的 OpenID,这是识别用户的关键。

  1. 配置网页授权域名:在微信公众平台后台“设置与开发”->“网页授权”中配置你的回调域名。
  2. 后端代码
@RestController
@RequestMapping("/oauth")
public class WxOAuth2Controller {
    @Autowired
    private WxMpService wxMpService;
    /**
     * 第一步:引导用户授权
     * 用户访问这个链接,会跳转到微信授权页面
     */
    @GetMapping("/authorize")
    public String authorize() {
        String redirectUri = "http://your-domain.com/oauth/callback"; // 你的回调地址
        // snsapi_base: 静默授权,获取 OpenID
        // snsapi_userinfo: 弹出授权,可获取用户信息
        String scope = "snsapi_base";
        return "redirect:" + wxMpService.oauth2buildAuthorizationUrl(redirectUri, scope, null);
    }
    /**
     * 第二步:回调处理
     * 微信会将 code 参数回调到这个地址
     */
    @GetMapping("/callback")
    public String callback(@RequestParam String code) {
        try {
            // 通过 code 换取 access_token
            WxMpOAuth2AccessToken accessToken = wxMpService.oauth2getAccessToken(code);
            // 通过 access_token 获取用户 OpenID
            String openId = accessToken.getOpenId();
            System.out.println("成功获取用户 OpenID: " + openId);
            // 你可以在这里根据 openId 进行登录、查询用户信息等操作
            return "用户 OpenID 是: " + openId;
        } catch (WxErrorException e) {
            return "获取 OpenID 失败: " + e.getError().getErrorMsg();
        }
    }
}

开发流程总结

  1. 准备阶段

    • 注册微信公众平台订阅号或服务号。
    • 获取 AppIDAppSecret
    • 开通开发者模式,配置服务器 URL 和 Token。
  2. 项目搭建

    • 创建一个 Spring Boot 项目。
    • 集成 WxJava 库。
  3. 配置信息

    • application.yml 中填写你的公众号配置信息。
  4. 消息处理

    • 创建一个 Controller,编写一个 POST 接口,路径与你在微信后台配置的 URL 一致。
    • 在该接口中,利用 WxJava 提供的工具类接收、解析、处理消息,并构造回复。
  5. 功能开发

    • 根据业务需求,调用 WxMpService 提供的各种 API(如菜单管理、用户管理、素材管理、模板消息等)。
    • 对于需要用户授权的功能(如网页授权),按照官方流程引导用户并处理回调。
  6. 测试与部署

    • 使用微信开发者工具或直接在手机上测试。
    • 将项目部署到公网服务器,确保微信服务器可以访问到你配置的 URL。

官方资源

希望这份详细的指南能帮助你顺利地在 Java 项目中集成微信公众平台 API!

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