微信公众号开发那些事

发布时间:2025-12-10 11:48:19 浏览次数:20

前言:最近接触的新项目是关于微信公众号的相关开发,其中会涉及到菜单栏的创建,配置跳转页面等等,在这过程中也是一边查资料一边摸索,也遇到一些坑,在这里分享下,如果有错误的地方或者写的不好的还请指点,也欢迎有疑问可以一起探讨。

一、准备工作

1. 微信SDK的选择

开发不一定就是自己造轮子,有优秀的开源的SDK那我们完全可以使用,这样可以避免采坑,还有繁杂的封装,从而提高开发效率 。这里我选择的是一位大佬的优秀SDK,作者一直在更新,目前已经来到了3.8.0版本。

1.1. SDK源码地址: github.

1.2. SDK部分Maven 依赖,可根据需求引入参考

<dependency><groupId>com.github.binarywang</groupId><artifactId>(根据不同需求引入参考以下模板)</artifactId><version>3.8.0</version></dependency>
ArtifactId
微信小程序:weixin-java-miniapp
微信支付:weixin-java-pay
微信开放平台:weixin-java-open
公众号(包括订阅号和服务号):weixin-java-mp
企业号/企业微信:weixin-java-cp
(由于我是公众号开发,这里用到的是 weixin-java-mp)

1.3. JAVA项目相关配置

1.3.1. 配置application.yml(详细信息及对应公众平台配置值见目录 2.公众平台相关配置)

business:config:wechat:appId: # 公众号appidsecret: # 公众号的appsecrettoken: # 接口配置里的Token值aesKey: # 接口配置里的EncodingAESKey值authUrl:

1.3.2. 配置SDK相关配置类
WeChatAppConfig.java 用来加载yml中的配置信息

import org.springframework.boot.context.properties.ConfigurationProperties;import org.springframework.context.annotation.Configuration;@Getter@Setter@Configuration@ConfigurationProperties(prefix = "business.config.wechat")public class WeChatAppConfig {/*** 设置微信公众号的appId*/private String appId;/*** 设置微信公众号的app secret*/private String secret;/*** 设置微信公众号的token*/private String token;/** 设置微信公众号的 EncodingAESKey*/private String aesKey;/*** 权限URL*/private String authUrl;}

SDK微信服务核心配置 WeChatMpConfig.java

import me.chanjar.weixin.common.api.WxConsts;import me.chanjar.weixin.mp.api.WxMpMessageRouter;import me.chanjar.weixin.mp.api.WxMpService;import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl;import me.chanjar.weixin.mp.config.WxMpConfigStorage;import me.chanjar.weixin.mp.config.impl.WxMpDefaultConfigImpl;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;import org.springframework.boot.context.properties.EnableConfigurationProperties;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;@Configuration@ConditionalOnClass(WxMpService.class)@EnableConfigurationProperties(WeChatAppConfig.class)public class WeChatMpConfig {@Autowiredprivate WeChatAppConfig weChatAppConfig;/*** 用户取消关注事件处理服务*/@Autowired //如果不需要用到微信的相关事件可以不用private UnsubscribeHandler unsubscribeHandler;@Bean@ConditionalOnMissingBeanpublic WxMpConfigStorage wxMpConfigStorage() {WxMpDefaultConfigImpl configStorage = new WxMpDefaultConfigImpl();configStorage.setAppId(weChatAppConfig.getAppId());configStorage.setSecret(weChatAppConfig.getSecret());configStorage.setToken(weChatAppConfig.getToken());configStorage.setAesKey(weChatAppConfig.getAesKey());return configStorage;}@Bean@ConditionalOnMissingBeanpublic WxMpService wxMpService(WxMpConfigStorage configStorage) {WxMpService wxMpService = new WxMpServiceImpl();wxMpService.setWxMpConfigStorage(configStorage);return wxMpService;}@Bean //如果不需要用到微信的相关事件可以不用配置此项public WxMpMessageRouter router(WxMpService wxMpService) {final WxMpMessageRouter newRouter = new WxMpMessageRouter(wxMpService);// 取消关注事件newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT).event(WxConsts.EventType.UNSUBSCRIBE).handler(this.getUnsubscribeHandler()).end();return newRouter;}protected UnsubscribeHandler getUnsubscribeHandler() {return this.unsubscribeHandler;}}

2. 公众号平台相关配置

微信公众号平台登录: 链接地址.

2.1. 公众号基本配置: 开发>基本配置

  • 开发者ID(AppID):application.yml配置中 -> appId
  • 开发者密码(AppSecret):application.yml配置中 -> secret,目前平台要求生成后密码由自己保存管理,如果忘记可以进行重置
  • IP白名单:对于去请求调用获取access_token 接口的服务端或者前端需要将其所在IP设置为白名单,多个IP地址配置可用回车键隔开
  • 服务器地址(URL):这里配置的是开发者服务器地址。用于处理用户被动回复消息(用户给公众号发消息,微信服务器会将消息发送到开发者设置的当前服务器地址,开发者进行消息验证,公众号进行回复),关注,取关,扫码等等一些事件,来响应微信服务器发送的一些验证或者消息
  • 令牌(Token):application.yml配置中 -> token
  • 消息加解密密钥(EncodingAESKey):application.yml配置中 -> aesKey
  • 消息加解密方式:根据业务需要可以选择消息加解密类型
  • 2.2. 公众号网页相关配置 公众号设置->功能设置

  • JS接口安全域名:这里设置的是前端页面所在域名,设置后开发者可以在公众号中该域名下调用微信开放的JS接口,只支持域名方式不支持IP地址,端口号等(如 wx.qq.com)不需要http,保存成功后需要点击下载生成的 MP_verify_*.txt 文件上传至填写的域名web服务器,详细可查看设置时平台给的提示
  • 网页授权域名:用户在网页授权同意后,微信会将授权数据传给一个回调页面,回调页面需在此域名下,以确保安全可靠,填写保存后同上点1需要上传MP_verify_*.txt 文件上传至填写的域名web服务器
  • 二、实际应用

    1.微信网页授权与回调

    这里只是一个简单里范例,看个人业务需求选择,SDK中的WxMpService 服务封装了各种常用的微信接口,可以参考git中的文档使用。这里的重定向使用的是MVC中的重定向方式:return "redirect:+url, 而Spring Boot使用了@RestController注解,以下写法只能返回字符串,可以将一个HttpServletResponse参数添加到处理程序方法然后调用response.sendRedirect(url)进行重定向。

    package com.wechat.controller;import me.chanjar.weixin.common.api.WxConsts;import me.chanjar.weixin.common.error.WxErrorException;import me.chanjar.weixin.mp.api.WxMpService;import me.chanjar.weixin.mp.bean.result.WxMpOAuth2AccessToken;import me.chanjar.weixin.mp.bean.result.WxMpUser;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestParam;import java.net.URLEncoder;@Controller@RequestMapping("/wechat")public class WechatAuthController {/*** sdk服务*/@Autowiredprivate WxMpService wxMpService;/*** 请求微信服务器认证授权(网页授权)** 用户在公众号中访问第三方的网页,公众号通过当前接口调用微信网页授权,用户确认同意授权后来获取用户基本信息。** 回调机制可根据个人业务需求:1.服务端->网页回调 2.服务端->服务端回调,我这里采用的是2方式,* 回调服务端来获取用户信息,也可以回调前端,前端根据返回的code在来调用获取用户信息接口** @param returnUrl 跳转回调页面地址* @return*/@GetMapping("/authorize")public String authorize(@RequestParam("returnUrl") String returnUrl) throws Exception {// 网页授权回调地址(这里可以是服务端接口,也可以是回调页面地址)String url = "https://你的域名/wechat/userInfo";String redirectURL = wxMpService.oauth2buildAuthorizationUrl(url,//网页授权回调地址,不管是页面回调,还是服务端接口的回调地址必须在微信平台配置的网页授权域名下,不然会回调失败WxConsts.OAuth2Scope.SNSAPI_USERINFO,//网页授权两种scope,一种是静默授权,一种是非静默方式URLEncoder.encode(returnUrl));//state参数,微信回调重定向会带上该参数,值是不变的,一般做校验用,这里我放的是前端的回调地址。return "redirect:" + redirectURL;//由于网页授权回调地址 url我这里是服务端的接口地址,会重定向到获取用户信息接口,获取相关用户信息}/*** 获取用户信息** @param code 用户code,使用后即失效,不使用5分钟失效,用于获取用户access_token的* @param returnUrl 网页授权时传入的state参数,回调时会原原本本返回* @return* @throws Exception*/@GetMapping("/userInfo")public String userInfo(@RequestParam("code") String code,@RequestParam("state") String returnUrl) throws Exception {WxMpOAuth2AccessToken wxMpOAuth2AccessToken;try {// 获取用户accessTokenwxMpOAuth2AccessToken = wxMpService.oauth2getAccessToken(code);// 获取用户信息WxMpUser wxMpUser = wxMpService.oauth2getUserInfo(wxMpOAuth2AccessToken, null);} catch (WxErrorException e) {e.printStackTrace();throw new Exception(e.getError().getErrorMsg());}// 获取openId,用户在一个公众号中的唯一标识String openId = wxMpOAuth2AccessToken.getOpenId();return "redirect:" + returnUrl + "?open/wechat/jsdk")public class WxJsdkController {/*** sdk服务*/@Autowiredprivate WxMpService wxMpService;@ResponseBody@RequestMapping(value = "/getTicket")public WxJsapiSignature getTicket(@RequestParam("url") String url) {try {WxJsapiSignature jsapi = wxMpService.createJsapiSignature(url);return jsapi;} catch (WxErrorException e) {e.printStackTrace();}// 获取失败return null;}}

    SDK中的创建签名方法中已经封装好,包括获取签名需要的时间戳,随机串,并且按照开发文档中的要求排序生成签名,保存ticket信息等。以上只是简单的案例,可以根据个人业务需求去封装一个结果,对获取失败进行处理,返回相关错误信息等。一下为WxJsapiSignature 类的数据格式:

    {"appId": "appid","nonceStr": "Rr5RUodeRn8BTArc","signature": "16f72c24f53d69199ac8af56c2a0786ff8baf04e","timestamp": 1596000000,"url": "https://www.xx.cn/test/test.html"}

    三、测试及遇到坑

    1.测试号的申请与配置

    在微信公众平台开发中,由于功能还在测试阶段需要调试,且微信公众号注册也有一定的门槛,微信公众平台提供了: 接口测试账号申请.用户使用微信登录后就有一个可测试的账号。

    1.1.微信回调本地服务配置,固定端口内网穿透

    根据申请到的测试号的相关配置 appId,secret,url,token,js域名,在平台中及项目中配置好,如果项目在本地,需要微信回调到本地服务,这时我们就需要下载安装一个ngrok.exe内网穿透工具.将本地项目对应的端口映射到一个可以访问的公网地址。下载安装后,工具的命令很简单,假如本地项目端口为8080,如下:
    使用cmd切换到ngrok.exe目录,执行命令 ngrok http + 需要映射的端口

    ngrok.exe http 8080

    回车后结果如下,会生成带时效的 session Expire 这里是 8小时的公网地址,有http和https格式,其域名都是一样的,切记当前窗口要保持开启才有效。将以下的域名配置到测试号平台中如js域名等,这样在请求微信网页授权时候传的回调地址里使用该地址+具体项目接口即可收到微信正常回调。

    1.2.菜单栏的添加

    毕竟是测试账号,不是正规军,它没有提供可视化的公众号菜单或按钮的编辑,所以如果你需要在测试号上添加相应菜单栏,那你只能通过在线通过 微信公众平台在线调试工具页面.去做添加和删除

    1.2.1.获取公众号access_token

    填写好测试号的appid与secret,点击检查问题请求获取

    请求结果如下,根据返回的access_token就可以去调用其它接口了

    1.2.2.根据access_token创建菜单

    根据1.2.1请求结果获取的access_token,调用菜单栏创建接口,菜单栏请求body为json格式,可参考自定义菜单创建接口文档中的格式.创建想要的菜单或按钮格式,微信规定最多只能创建3个菜单,每个菜单最多只能有5个子菜单,以下为创建示例,编辑和删除大同小异,这里就不展开了

    请求结果

    1.2.2.查看效果

    申请的微信测试号即使是通过自己微信号申请,自己也可以扫码关注,通过微信扫码关注就可以看到菜单栏创建的效果,点击可以根据body参数中配置url跳转

    2.踩过的坑

    2.1.1.回调地址url问题

    项目测试其,曾把回调地址配置成服务端地址,报错了:redirect_uri域名与后台配置不一致,提示已经很明显了,如果是在PC端访问或者安卓端访问会出现页面跳转不过去,只有ios会有这个错误的弹窗提示,所以如果是非ios可能还不好定位到问题,页面也一直卡主不跳转。由于项目是前后端分离,且前端部署在公众平台配置的域名服务器中,而服务端部署在另外的服务器下导致,这种情况解决的办法有两种,将回调改成前端页面回调方式,或者配置nginx代理。

    2.1.2.JS-SDK签名url问题

    (1).当调用服务端获取签名数据时,正常返回的签名数据,而当前端使用数据调用微信JS分享提示 invalid signature 错误,由于使用的是比较成熟的SDK,且核对了配置可以基本排除以下几个可能:

  • .config 中的 appid 与用来获取 jsapi_ticket 的 appid 不一致
  • config中nonceStr, timestamp与用以签名中的对应noncestr, timestamp不一致
  • 没有缓存access_token和jsapi_ticket
  • (2).接下来是利用 在线微信JS接口签名校验工具 验证签名是否正确


    jsapi_ticket 可以通过微信sdk服务打印获取:

    String ticket = wxMpService.getTicket(TicketType.JSAPI, false);

    通过验证,生成的签名和接口中返回的签名是一致的,排除签名错误的可能

    (3).确认微信公众平台是否配置了IP白名单

    获取access_token服务端的IP和前端的IP需要配置在白名单中,否则也会出现该为题

    (4).JS签名数据接口中,参数url问题

    排除里以上问题,可以说基本是url的问题了,一开始核实了以上都没有问题,且在微信的开发文档中按照要求也不觉得是url问题,文档中也说明 url不进行转义,所以在创建时我一直是原参数url创建,此时已经要脑裂,也翻阅了好多网上资料,没有找到问题,最后试了下 将url encode一次后再进行签名创建,结果成功了。也有可能我的url后面是带参数的问题导致,但是文档中并没有提示要进行encode,而是提示不进行转义,这个坑有点深。成功后我的第一件事当然是去开发社区吐槽一波。至此该问题得到了解决。

    【文章只是个人经历,写的不好或者有误的地方还请指点,有疑问可以一起探讨】

    需要做网站?需要网络推广?欢迎咨询客户经理 13272073477