杰瑞科技汇

Java教程从哪开始学?微信公众号怎么选?

目录

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

基础准备:了解微信公众号

在开始编码前,你必须先理解微信公众号的基本概念。

Java教程从哪开始学?微信公众号怎么选?-图1
(图片来源网络,侵删)

1 公众号的类型

  • 订阅号:主要用于信息传播,适合媒体、个人等,每天可以推送一次消息,消息在订阅号文件夹中。
  • 服务号:主要为用户提供服务,适合企业、政府等,每月可以推送四次消息,消息会直接出现在聊天列表中,并可使用更多高级接口(如微信支付、JS-SDK等)。
  • 小程序:一个独立的应用形态,不是公众号,但可以与公众号关联。

对于开发者来说,最重要的是:你必须是该公众号的“开发者”,并拥有 AppIDAppSecret

2 开发者模式

微信提供了两种模式来处理用户消息:

  • 编辑模式:通过网页可视化配置,适合非开发者,无法进行复杂的逻辑处理。
  • 开发模式:开发者通过自己的服务器来接收和处理所有用户消息,并进行回复,这是我们本教程的重点。

要开启开发模式,你需要一个公网可以访问的服务器地址(URL),用于接收微信服务器发来的消息。


环境搭建:你的第一个 Java 项目

1 技术栈选择

  • 框架Spring Boot (强烈推荐),它能极大地简化项目配置和依赖管理。
  • 构建工具:Maven 或 Gradle。
  • 服务器:内嵌 Tomcat (由 Spring Boot 提供)。
  • JSON 处理:Jackson (Spring Boot 默认集成)。

2 创建 Spring Boot 项目

最简单的方式是使用 Spring Initializr

Java教程从哪开始学?微信公众号怎么选?-图2
(图片来源网络,侵删)
  1. Project: Maven Project
  2. Language: Java
  3. Spring Boot: 选择一个稳定版本 (如 2.7.x 或 3.x.x)
  4. Project Metadata:
    • Group: com.example
    • Artifact: wechat-demo
    • Name: wechat-demo
    • Packaging: Jar
    • Java: 8 或更高版本
  5. Dependencies: 添加 Spring Web 依赖。
  6. 点击 "GENERATE" 下载项目压缩包,并用你的 IDE (如 IntelliJ IDEA 或 Eclipse) 打开。

3 配置服务器信息

  1. 登录 微信公众平台
  2. 进入 “设置与开发” -> “基本配置”。
  3. 找到 “服务器配置”,点击 “修改配置”。
  4. 填写以下信息:
    • URL: 这是你的服务器地址,必须公网可访问。http://yourdomain.com/wechat/yourToken,我们稍后会创建这个接口。
    • Token: 由你自定义的字符串,用于验证请求是否来自微信服务器。
    • EncodingAESKey: 随机生成或手动填写,用于消息加解密,初期可以先留空,选择 “安全模式” 或 “兼容模式”。
    • 消息加解密方式: 选择 “安全模式” 或 “兼容模式”。
  5. 点击 “提交”,微信服务器会向你的 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

Java教程从哪开始学?微信公众号怎么选?-图3
(图片来源网络,侵删)

你需要做三件事来验证:

  1. tokentimestampnonce 三个参数进行字典序排序。
  2. 将三个参数字符串拼接成一个字符串进行 SHA1 加密。
  3. 将加密后的字符串与 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 非常繁琐且容易出错,强烈建议使用 XStreamJackson 的 XML 模块来处理,市面上也有成熟的微信 SDK,如 weixin-java-mp,它们已经封装好了这些解析逻辑。


核心功能二:被动回复与主动发送

1 被动回复用户消息

上面 handleWeChatMessage 的例子就是 被动回复,你的服务器必须在 5 秒内返回响应,否则微信会认为请求失败,被动回复是针对用户特定消息的即时回复。

2 主动发送消息(客服消息)

除了被动回复,你还可以 主动 给用户发送消息,

  • 用户关注公众号后,发送欢迎语。
  • 用户完成某个操作后,发送通知。
  • 定期推送活动信息。

这需要使用“客服消息接口”。

步骤:

  1. 获取 Access Token (见下一节)。
  2. 构造 JSON 请求体
  3. 向微信 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小时,你不能每次调用接口都去重新获取,最佳实践是:

  1. 本地缓存:首次请求时,从微信服务器获取 Token 并存入本地缓存(如 ConcurrentHashMap 或 Redis)。
  2. 设置过期时间:同时设置一个比2小时稍短(如1小时50分)的过期时间。
  3. 检查缓存:每次需要 Token 时,先检查缓存中是否存在且未过期。
  4. 刷新缓存:如果缓存中没有或已过期,则重新获取并更新缓存。

代码示例 (使用 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 授权流程

  1. 引导用户授权:你的网页构造一个重定向 URL,包含 appid, redirect_uri, scope, state 等参数,让用户跳转到微信的授权页面。
    • snsapi_base: 静默授权,只获取 openid
    • snsapi_userinfo: 弹出授权页面,可获取 openid 和用户基本信息(昵称、头像等)。
  2. 用户同意授权:用户点击同意后,微信会重定向到你的 redirect_uri,并附上 codestate 参数。
  3. 获取 access_token: 你的服务器用 code 向微信服务器换取 access_tokenopenid
  4. 获取用户信息: scopesnsapi_userinfo,则可以用上一步的 access_tokenopenid 获取用户信息。

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 开发之旅!祝你编码愉快!

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