介绍微信公众号开发文档服务号介绍 | 微信服务号文档微信公众号管理后台界面微信公众平台微信公众号测试号管理后台界面微信公众平台微信开发者工具下载地址微信开放文档开发笔记参考https://blog.csdn.net/midyear/category_12378053.html微信开发者工具使用下载地址微信开放文档使用流程打开【微信开发者工具】选择【公众号网页调试】将调试页面地址复制到如下即可本地调试页面内网穿透工具使用 cploar 官网地址cpolar官网-安全的内网穿透工具 | 无需公网ip | 远程访问 | 搭建网站什么是内网穿透工具在微信开发中我们必须将代码部署在公网服务器上才能调试说白了就是需要微信端能够调用到你的服务器内网穿透工具能够将本地端口映射到它提供的一个公网域名上通过访问这个域名来实现本地方法的调用公众号测试号配置URL 和 Token 配置url: 必须以http://或https://开头分别支持80端口和443端口 URL规范比如项目的基础目录为 www.abc.com/wechat/则URL地址为 www.abc.com/wechat token: 可由开发者可以任意填写用作生成签名该Token会和接口URL中包含的Token进行比对从而验证安全性配置 JS接口安全域名JS接口安全域名 就是当我们调用微信开发的JS接口时使用的域名必须与配置的 JS接口安全域名一致 这个域名可以不与上面的URL一致配置 授权回调页面域名授权回调页面域名: 当我们在跳转的页面中需要调用微信公众号的接口就必须配置这个域名地址 这个域名可以不与上面的URL一致 具体使用细节参考https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html在当前测试号页面可以看到 appId 和 secret 配置完后就可以在项目中使用微信公众号测试号了。公众号配置在公众号中配置 URL 和 token在【基本配置】中配置配置 JS域名地址 和 授权回调域名地址在【公众号设置】-【功能设置】中配置在配置 JS域名地址 和 授权回调域名地址还需要在服务上配置一个txt文件然后通过配置的域名 xxx.txt能访问到。通常这个可以使用 nginx 转发实现。nginx 配置。将文件放置在 d:/webapps/wx/目录下location /MP_verify_n5GSFAcyM6FNnI9L.txt { alias d:/webapps/wx/; index MP_verify_n5GSFAcyM6FNnI9L.txt; }微信认证接口在配置 URL 对应的 controller 下需要添加微信认证认证接口是一个get请求且路径只能配置为 //** * 微信认证 * * param signature * param timestamp * param nonce * param echostr * return */ GetMapping(/) ResponseBody public String authWechat(String signature, String timestamp, String nonce, String echostr) { log.info(微信请求参数[signature{}, timestamp{}, nonce{}, echostr{}], signature, timestamp, nonce, echostr); if (CommonUtil.isAnyBlank(signature, timestamp, nonce, echostr)) { throw new RuntimeException(请求参数非法); } if (!checkSignature(signature, timestamp, nonce)) { throw new RuntimeException(请求参数非法); } return echostr; } /** * 检查signature * * param timestamp * param nonce * return */ public boolean checkSignature(String signature, String timestamp, String nonce) { StringBuilder stringBuilder new StringBuilder(); // 自然排序字符串拼接 Stream.of(token, timestamp, nonce) .sorted() .forEach(stringBuilder::append); String encryptSHA1 CommonUtil.encryptSHA1(stringBuilder.toString()); return Objects.equals(encryptSHA1, signature); }CommonUtil工具类public class CommonUtil { /** * 是否存在空白的数据 * * param params * return */ public static boolean isAnyBlank(String... params) { for (String param : params) { if (!StringUtils.hasText(param)) { return true; } } return false; } /** * sha1 加密 * * param str * return */ public static String encryptSHA1(String str) { try { MessageDigest sha1 MessageDigest.getInstance(SHA-1); byte[] hashedBytes sha1.digest(str.getBytes()); StringBuilder sb new StringBuilder(); for (byte b : hashedBytes) { sb.append(String.format(%02x, b)); } return sb.toString(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } return null; } }微信事件处理接口在配置 URL 对应的 controller 下需要添加微信事件处理认证接口是一个post请求且路径只能配置为 //** * 接收普通消息/事件 * * param request * return */ PostMapping(/) ResponseBody public String receiveMessage(HttpServletRequest request) { MapString, String map new HashMap(); try (ServletInputStream inputStream request.getInputStream()) { SAXReader saxReader new SAXReader(); Document document saxReader.read(inputStream); // 获取root Element root document.getRootElement(); // 获得所有子节点 ListElement elements root.elements(); for (Element element : elements) { map.put(element.getName(), element.getStringValue()); } } catch (IOException | DocumentException e) { throw new RuntimeException(e); } System.out.println(map); // 回复消息 return replyMessage(map); } /** * 回复消息 * * param map * return */ private String replyMessage(MapString, String map) { String msgType map.get(MsgType); String message ; switch (msgType) { case text: // TODO 消息 break; case event: // TODO 事件 break; default: message commonReply(map, 欢迎你使用公众号); break; } return message; }解析 xml 使用到了 xstream maven依赖dependency groupIdcom.thoughtworks.xstream/groupId artifactIdxstream/artifactId version1.4.20/version /dependencyxstream拓展除了解析 xml 它还提供了java对象转 xml代码如下代转 xml 的对象Data XStreamAlias(xml) public class TextMessage { /** * 接收方账号 */ XStreamAlias(ToUserName) private String toUserName; /** * 开发者微信号 */ XStreamAlias(FromUserName) private String fromUserName; /** * 消息创建时间 整型 */ XStreamAlias(CreateTime) private Long createTime; /** * 消息类型 */ XStreamAlias(MsgType) private String msgType; /** * 回复的消息内容 */ XStreamAlias(Content) private String content; }在实际代码中转 xmlprivate String commonReply(MapString, String map, String content) { TextMessage textMessage new TextMessage(); textMessage.setToUserName(map.get(FromUserName)); textMessage.setFromUserName(map.get(ToUserName)); textMessage.setMsgType(text); textMessage.setCreateTime(System.currentTimeMillis() / 1000); textMessage.setContent(content); // 将java对象转换成xml对象 XStream xStream new XStream(); xStream.processAnnotations(TextMessage.class); return xStream.toXML(textMessage); }UnionID、OpenID、AppID区别UnionID在微信公众平台和微信开放平台中对于同一个微信用户无论其通过多少个不同的应用App或公众号登录都会对应一个唯一的 UnionIDOpenID每个应用App或公众号下同一个微信用户的OpenID是不同的。这意味着即使同一个用户在不同的应用或公众号中登录每个应用或公众号会为该用户分配一个独特的OpenIDAppID每个微信应用包括小程序、公众号等都有一个唯一的appID用于标识该应用遇到的问题前后端部署在两个服务器存在跨域问题当前后端部署在两个服务器中会存在跨域问题这个时候可以在 controller 上面配置CrossOrigin注解从而允许来自该域名的跨域请求RestController RequestMapping(/wechat/) CrossOrigin(origins http://net.abc.com) // 允许来自该域名的跨域请求 public class WxBaseConfigController { ... }微信扫描生成的二维码微信扫描生成的二维码有两种情况已经关注了公众号这时候扫描会进入【微信事件处理接口】条件为// xxx 表示生成二维码携带的内容 if (subscribe.equals(map.get(Event)) Objects.equals(map.get(EventKey), xxx)) { // TODO 业务处理 }未关注公众号这个时候扫描也会进入【微信事件处理接口】但条件变为// 此时 EventKey 的内容等于 qrscene_ xxx其中xxx为二维码携带的内容 if (SCAN.equals(map.get(Event)) Objects.equals(map.get(EventKey), qrscene_xxx)) { // TODO 业务处理 }生成公众号二维码参考生成带参数的二维码 | 微信服务号文档微信公众号将当前页面转成图片并下载前置条件微信公众号前端使用的是 vue3 写的我使用的是 vue 的 html2canvas 实现的部分代码如下script setup langts import html2canvas from html2canvas; const btnClickDownload () { var content document.getElementById(flexGird); if (content) { app.data.showPic true; html2canvas(content).then(canvas { //添加属性 canvas.setAttribute(id, thecanvas); var base64Image document.getElementById(base64Img); const img canvas.toDataURL(image/png); // 将canvas转换为base64图片格式 if (base64Image) { base64Image.setAttribute(src, img); } }); } }; /script template div div classcontainer idflexGird div需要生成图片的页面/div /div div classbtn-box div classbutton clickbtnClickDownload下载图片/div /div /div /template style scoped /styleIP不在白名单中在对接微信公众号时调用接口报错errmsg: **invalid ip xxx.xxx.xxx.xxx** ipv6 ::ffff:xxx.xxx.xxx.xxx, not in whitelist rid: 6280cf55-5sccscb42-7cscc91f0第一种情况配置了错误的 appId 和 secret这种情况也可能报这种错误解决方案修正 appId 和 secret 即可第二种情况该问题是由于微信公众需要调用方的IP在白名单中才可以正常调用解决方案打开微信公众号在【基础配置】中的 【IP白名单】中的配置报错的IP即可注意事项马上配置完可能还会报同样的错误这个时候是因为微信公众号更新没有实时的原因可以稍微等一下再试试