目录

1. 概念说明:

2. 开发准备:

3. 测试demo(更改配置信息即可使用)

3.1. 服务器配置

3.1.1.配置填写说明

3.1.2.校验服务器有效性:

3.1.3.URL后端接口代码和校验代码(servlet)

3.1.4.配置内网穿透,完成本地调试

3.1.5. 可能存在的问题

3.2 模板消息

3.2.1. 搞定 template_id 即模板消息id:

3.2.2. 搞定 touser 即openid

3.2.3. 从获取openid的请求中我们发现需要access_token:

3.2.4. 发送模板消息的url参数

3.2.5. topcolor

3.2.5. data

3.3. 源码

3.3.1 模板消息DTO

3.3.2. 模板消息内容DTO

3.3.3. access_token缓存类:

3.3.4.http请求工具类:

3.3.5. 最终的Servlet(controller自行转换)(为方便观看所有逻辑都写在这里了,自行优化):

3.4.测试

官方文档:

微信公众平台开发概述 | 微信开放文档

全局返回码文档 :微信开放文档

1. 概念说明:

  • access_token:是公众号的全局唯一接口调用凭据,公众号调用各接口的必要参数(2小时内有效,过期需要重新获取,但1天内获取次数有限,需自行存储)

  • OpenID :为了识别用户每个公众号针对,每个用户会产生一个OpenID(用户id:对用户的操作需要用到)

  • UnionID: 同一开放平台账号下不同公众号或应用下用户的共同id(这里不需要用到)

  • 消息会话(这里用到模板消息)

    • 公众号是以微信用户的一个联系人形式存在的,消息会话是公众号与用户交互的基础。

    • 公众号内主要有这样几类消息服务的类型,分别用于不同的场景:

      • 群发消息:订阅号为每天1次,服务号为每月4次

      • 被动回复消息:在用户给公众号发消息后,公众号可以回复一个消息

      • 客服消息:用户在公众号内发消息/触发特定行为后,公众号可以给用户发消息

      • 模板消息:在需要对用户发送服务通知(如刷卡提醒、服务预约成功通知等)时,公众号可以用特定内容模板,主动向用户发送消息。

2. 开发准备:

  1. 开发者在公众平台网站中创建服务号、获取接口权限后方可开始

    https://kf.qq.com/faq/120911VrYVrA150918fMZ77R.html?scene_id=kf3386

  2. 个人研究测试:通过手机微信扫描二维码获得测试号

    https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login

3. 测试demo(更改配置信息即可使用)

3.1. 服务器配置

这里坑比较多,服务器配置不是随便填一个url就完事了,需要后端接口配合校验。

界面:

测试号界面:

3.1.1.配置填写说明

  • URL:服务器地址--是开发者用来接收微信消息和事件的接口URL (在提交配置修改时微信会向该URL接口发送请求验证服务器地址的有效性)

  • Token:任意填写,用作生成签名(微信向上述URL接口发送的请求是携带token的,需要在接口中校验token一致以确保安全性)这个token与上述的access_token不是一回事。这个token只用于验证开发者服务器。

  • EncodingAESKey: 由开发者手动填写或随机生成,将用作消息体加解密密钥

  • 消息加解密方式 :明文模式、兼容模式和安全模式

3.1.2.校验服务器有效性:

这是重点:点击完提交修改后,微信会向url发起一个请求并将token携带过去,这个请求要能正确被你的后端服务器所响应并返回正确的结果,服务器配置才算修改成功

校验请求说明:

  • 开发者提交信息后,微信服务器将发送GET请求到填写的服务器地址URL上,GET请求携带参数如下表所示:

    • signature:微信加密签名,signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数。

    • timestamp:时间戳

    • nonce :随机数

    • echostr:随机字符串

  • 开发者通过检验signature对请求进行校验(下面有校验方式)。若确认此次GET请求来自微信服务器,请原样返回echostr参数内容,则接入生效,成为开发者成功,否则接入失败。

3.1.3.URL后端接口代码和校验代码(servlet)


@WebServlet(urlPatterns = {"/wx"})
public class WxServlet extends HttpServlet {// 服务器配置填写的tokenprivate static final String wxToken = "888888";@Overrideprotected void doGET(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {doGetWx(request, response);}
/*** @Description 校验配置URL服务器的合法性* @date 2023年5月29日下午4:17:40* @param request* @param response* @throws ServletException* @throws IOException*/public void doGetWx(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {String signature = request.getParameter("signature");String timestamp = request.getParameter("timestamp");String nonce = request.getParameter("nonce");String echostr = request.getParameter("echostr");// 将微信echostr返回给微信服务器try (OutputStream os = response.getOutputStream()) {String sha1 = getSHA1(wxToken, timestamp, nonce, "");// 和signature进行对比if (sha1.equals(signature)) {// 返回echostr给微信os.write(URLEncoder.encode(echostr, "UTF-8").getBytes());os.flush();}} catch (Exception e) {e.printStackTrace();}}/*** 用SHA1算法生成安全签名** @param token     票据* @param timestamp 时间戳* @param nonce     随机字符串* @param encrypt   密文* @return 安全签名* @throws Exception*/public static String getSHA1(String token, String timestamp, String nonce, String encrypt) throws Exception {try {String[] array = new String[] { token, timestamp, nonce, encrypt };StringBuffer sb = new StringBuffer();// 字符串排序Arrays.sort(array);for (int i = 0; i < 4; i++) {sb.append(array[i]);}String str = sb.toString();// SHA1签名生成MessageDigest md = MessageDigest.getInstance("SHA-1");md.update(str.getBytes());byte[] digest = md.digest();StringBuffer hexstr = new StringBuffer();String shaHex = "";for (int i = 0; i < digest.length; i++) {shaHex = Integer.toHexString(digest[i] & 0xFF);if (shaHex.length() < 2) {hexstr.append(0);}hexstr.append(shaHex);}return hexstr.toString();} catch (Exception e) {e.printStackTrace();}return "";}}

项目路径是/xjsrm,因此服务器url地址就是:http://localhost:8080/xjsrm/wx

直接将http://localhost:8080/xjsrm/wx地址填到服务配置的url可以吗?答案是不可以!

3.1.4.配置内网穿透,完成本地调试

3.1.4.1. 内网穿透的必要性:

  • 微信需要检验服务器有效性,因此这个url必须是公网资源,就是外网能访问的,不支持本地127.0.0.1/localhost ,而且这样调试起来非常的不方便。
  • 因此这里使用内网穿透将本地资源映射到公网。(直接部署到服务器上调试的可以跳过)

 3.1.4.2. 用到的工具cpolar:

  • 也可以使用花生壳、natapp、ngrok等;但不建议使用natapp、ngrok,这两个工具都不能直接访问到服务资源,中间会多一层手动校验(提示用户是否要访问该网站),会造成不必要的麻烦。

 3.1.4.3. cpolar配置内网穿透的教程

  • 参考大佬的博文 从 2.内网穿透开始看到3.测试公网访问 即可微信公众号本地开发调试 - 无公网IP,内网穿透_微信公众号服务器调试_热爱编程的小K的博客-CSDN博客微信公众号本地开发调试 - 无公网IP,内网穿透https://blog.csdn.net/qq_72157449/article/details/130237603

 3.1.4.4. 获取本地项目的公网路径

        配置完cpolar后在在线隧道列表中找到本地项目的地址,我项目是localhost:8080,因此公网地址对应(最好用https协议)https://22717eef.r6.vip.cpolar.cn 、

因此完整的服务器Url就是: https://22717eef.r6.vip.cpolar.cn/xjsrm/wx

浏览器访问该url,看后端是否接受到了请求,如果接收到说明接口没问题,此时将URL填到对应的配置栏中点击提交即可。

 3.1.5. 可能存在的问题

如果你在使用点击修改配置的提交发现微信发送的请求根本就没有进到后端的项目中

此时查看sandboxinfo这个包。如果出现 errorcode=-1多半是内网穿透工具的问题。

出现其他错误代码可以去错误大全中根据信息排查

ngrok错误排查示例:         

3.2 模板消息

模板消息的官方文档:微信公众平台 (qq.com)

从官方给出的请求示例入手,看需要准备的接口和数据:

POST请求https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=ACCESS_TOKEN
请求包为一个json:{"template_id":"ngqIpbwh8bUfcSsECmogfXcV14J0tQlEpBO27izEYtY","touser":"OPENID","url":"http://weixin.qq.com/download","topcolor":"#FF0000","data":{"User": {"value":"黄先生","color":"#173177"},"Date":{"value":"06月07日 19时24分","color":"#173177"},"CardNumber": {"value":"0426","color":"#173177"},"Type":{"value":"消费","color":"#173177"},"Money":{"value":"人民币260.00元","color":"#173177"},"DeadTime":{"value":"06月07日19时24分","color":"#173177"},"Left":{"value":"6504.09","color":"#173177"}}
}

3.2.1. 搞定 template_id 即模板消息id:

新增模板消息(以测试号为例)

模板内容可设置参数(模板标题不可),供接口调用时使用,参数需以{{开头,以.DATA}}结尾(具体传参看后续代码)

3.2.2. 搞定 touser 即openid

查看用户管理的官方文档;微信开放文档 (qq.com),通过官方接口获取

因为我们是公众号,所以选用获取用户列表的接口:微信开放文档 (qq.com)

http请求方式: GET(请使用https协议)
https://api.weixin.qq.com/cgi-bin/user/get?access_token=ACCESS_TOKEN&next_openid=NEXT_OPENID参数    是否必须    说明
access_token    是   调用接口凭证
next_openid 是   第一个拉取的OPENID,不填默认从头开始拉取返回说明
正确时返回JSON数据包:{"total":2,"count":2,"data":{"openid":["OPENID1","OPENID2"]},"next_openid":"NEXT_OPENID"
}

3.2.3. 从获取openid的请求中我们发现需要access_token:

获取access_token官方文档:微信开放文档 (qq.com)

https请求方式: GET https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET参数说明参数    是否必须    说明
grant_type  是   获取access_token填写client_credential
appid   是   第三方用户唯一凭证
secret  是   第三方用户唯一凭证密钥,即appsecret
返回说明正常情况下,微信会返回下述JSON数据包给公众号:
{"access_token":"ACCESS_TOKEN","expires_in":7200}

3.2.4. 发送模板消息的url参数

这个是发送消息后用户点击卡片跳转的地址(自定义)可以不填

3.2.5. topcolor

消息卡片的顶部颜色(自定义)

3.2.5. data

消息的内容(了解结构即可,后续代码中会体现)

3.3. 源码

拷贝完再理解

3.3.1 模板消息DTO

import java.util.Map;/**
* @Description 微信公众号模板消息请求对象
* @author isymi
* @version
* @date 2023年5月29日下午4:28:09
**/
public class TemplateMessage {/*** 发送消息用户的openid*/private String touser;/** 模板消息id*/private String template_id;/*** 点击模板信息跳转地址;置空:则在发送后,点击模板消息会进入一个空白页面(ios),或无法点击(android)*/private String url;/*** 卡片顶部颜色*/private String topcolor;/*** key为模板中参数内容"xx.DATA"的xx,value为参数对应具体的值和颜色 */private Map<String, WeChatTemplateMsg> data;// private String data;public TemplateMessage() {}public TemplateMessage(String touser, String template_id, String url, String topcolor, Map<String, WeChatTemplateMsg> data) {this.touser = touser;this.template_id = template_id;this.url = url;this.topcolor = topcolor;this.data = data;}public String getTouser() {return touser;}public void setTouser(String touser) {this.touser = touser;}public String gettemplate_id() {return template_id;}public void settemplate_id(String template_id) {this.template_id = template_id;}public String getUrl() {return url;}public void setUrl(String url) {this.url = url;}public String getTopcolor() {return topcolor;}public void setTopcolor(String topcolor) {this.topcolor = topcolor;}public Map<String, WeChatTemplateMsg> getData() {return data;}public void setData(Map<String, WeChatTemplateMsg> data) {this.data = data;}@Overridepublic String toString() {return "TemplateMessage [touser=" + touser + ", template_id=" + template_id + ", url=" + url + ", topcolor="+ topcolor + ", data=" + data + "]";}}

3.3.2. 模板消息内容DTO


import java.io.Serializable;/**
* @Description 模板消息内容类
* @author isymi
* @version
* @date 2023年5月29日下午4:33:27
**/
public class WeChatTemplateMsg implements Serializable{/*** 消息实参*/private String value;/*** 消息颜色*/private String color;public WeChatTemplateMsg(String value) {this.value = value;this.color = "#173177";}public WeChatTemplateMsg(String value, String color) {this.value = value;this.color = color;}@Overridepublic String toString() {return "WeChatTemplateMsg [value=" + value + ", color=" + color + "]";}public String getValue() {return value;}public void setValue(String value) {this.value = value;}public String getColor() {return color;}public void setColor(String color) {this.color = color;}}

3.3.3. access_token缓存类:


/**
* @Description access_token缓存类
* @author
* @version
* @date 2023年5月30日上午10:40:08
**/
public class AccessToken {private String accessToken;//过期时间 当前系统时间+微信传来的过期时间private Long expiresTime;public AccessToken(String accessToken, String expiresIn) {this.accessToken = accessToken;this.expiresTime = System.currentTimeMillis()+Integer.parseInt(expiresIn)*1000;}/*** 判断token是否过期* @return*/public boolean isExpired(){return System.currentTimeMillis()>expiresTime;}public String getAccessToken() {return accessToken;}public void setAccessToken(String accessToken) {this.accessToken = accessToken;}public Long getExpiresTime() {return expiresTime;}public void setExpiresTime(Long expiresTime) {this.expiresTime = expiresTime;}public AccessToken(String accessToken, Long expiresTime) {this.accessToken = accessToken;this.expiresTime = expiresTime;}public AccessToken() {}}

3.3.4.http请求工具类:

import java.io.BufferedReader;import java.net.*;
import java.nio.charset.StandardCharsets;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.xx.xx.pojo.TemplateMessage;/**
* @Description 微信公众号http请求工具类
* @author isymi
* @version
* @date 2023年5月29日下午4:07:39
**/
public class WXPublicAccountHttpUtil {/*** @Description 根据请求获取返回结果字符串(根据请求获取accessToken)* @date 2023年5月29日下午4:04:21* @param url* @return* @throws IOException*/public static String get(String url) throws IOException {HttpURLConnection connection = null;BufferedReader reader = null;try {URL requestUrl = new URL(url);connection = (HttpURLConnection) requestUrl.openConnection();connection.setRequestMethod("GET");int responseCode = connection.getResponseCode();if (responseCode == HttpURLConnection.HTTP_OK) {reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));StringBuilder response = new StringBuilder();String line;while ((line = reader.readLine()) != null) {response.append(line);}return response.toString();} else {// Handle error responseSystem.out.println("HTTP GET request failed with response code: " + responseCode);return null;}} finally {if (reader != null) {reader.close();}if (connection != null) {connection.disconnect();}}}/*** @Description 根据URl获取JSONObject:根据请求获取关注用户列表数据* @date 2023年5月29日下午4:02:16* @param url* @return* @throws IOException*/public static JSONObject getJsonObject(String url) throws IOException {HttpURLConnection connection = null;BufferedReader reader = null;try {URL urlObj = new URL(url);connection = (HttpURLConnection) urlObj.openConnection();connection.setRequestMethod("GET");StringBuilder response = new StringBuilder();reader = new BufferedReader(new InputStreamReader(connection.getInputStream(), "UTF-8"));String line;while ((line = reader.readLine()) != null) {response.append(line);}/**    正确返回的格式*      {"total":2,"count":2,"data":{"openid":["OPENID1","OPENID2"]},"next_openid":"NEXT_OPENID"}*/return JSON.parseObject(response.toString());} finally {if (reader != null) {reader.close();}if (connection != null) {connection.disconnect();}}}/*** @Description 获取关注用户的 openid 集合* @date 2023年5月29日下午4:04:02* @param url* @return* @throws IOException*/public static  List<String> getOpenidList(String url) throws IOException {// 获取关注用户列表数据JSONObject jsonObject = getJsonObject(url);System.out.println(jsonObject);// 错误情况if (jsonObject.containsKey("errcode")) {int errcode = jsonObject.getIntValue("errcode");String errmsg = jsonObject.getString("errmsg");throw new RuntimeException("Failed to get openid list. errcode: " + errcode + ", errmsg: " + errmsg);}int total = jsonObject.getIntValue("total");// 无用户关注 {"total":0,"count":0,"next_openid":""}if (total == 0) {throw new RuntimeException("No openid found. Total is 0.");}// 有用户关注:/*** {"total":1,*    "data":{*         "openid":["o-tgG5-VaQfsgdjerHA-z2PeZFls"]},*        "count":1,*       "next_openid":"o-tgG5-VaQfsgdjerHA-z2PeZFls"}*/JSONObject dataObject = jsonObject.getJSONObject("data");int count = dataObject.getIntValue("count");System.out.println("关注总人数:"+count);JSONArray openidArray = dataObject.getJSONArray("openid");// 将 openid 数组封装为 List 集合List<String> openidList = new ArrayList<>();for (int i = 0; i < openidArray.size(); i++) {String openid = openidArray.getString(i);openidList.add(openid);}return openidList;}/*** @Description 发送消息* @date 2023年5月29日下午4:58:02* @param accessToken* @param templateMessage* @return* @throws IOException*/public static String sendMessage( String accessToken, TemplateMessage templateMessage) throws IOException {String requestUrl ="https://api.weixin.qq.com/cgi-bin/message/template/send" + "?access_token=" + accessToken;URL urlObject = new URL(requestUrl);HttpURLConnection connection = (HttpURLConnection) urlObject.openConnection();connection.setRequestMethod("POST");connection.setDoOutput(true);connection.setRequestProperty("Content-Type", "application/json");String requestBody = JSON.toJSONString(templateMessage);byte[] requestBodyBytes = requestBody.getBytes(StandardCharsets.UTF_8);connection.setRequestProperty("Content-Length", String.valueOf(requestBodyBytes.length));OutputStream outputStream = connection.getOutputStream();outputStream.write(requestBodyBytes);outputStream.close();int responseCode = connection.getResponseCode();BufferedReader reader;if (responseCode >= 200 && responseCode <= 299) {reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));} else {reader = new BufferedReader(new InputStreamReader(connection.getErrorStream()));}StringBuilder response = new StringBuilder();String line;while ((line = reader.readLine()) != null) {response.append(line);}reader.close();connection.disconnect();System.out.println("Response Code: " + responseCode);System.out.println("Response Body: " + response.toString());return response.toString();}}

3.3.5. 最终的Servlet(controller自行转换)(为方便观看所有逻辑都写在这里了,自行优化):

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.xx.srm.pojo.AccessToken;
import com.xx.xx.pojo.TemplateMessage;
import com.xx.xx.pojo.WeChatTemplateMsg;
import com.xx.xx.utils.WXPublicAccountHttpUtil;@WebServlet(urlPatterns = {"/wx", "/wx/message" })
public class WxServlet extends HttpServlet {// 必须替换private static final String wxToken = "xxxxxm8";// 必须替换public static final String APPID = "wx3xxxxxx1795fa";// 必须替换public static final String SECRET = "57b96fxxxxxxxxeab62bfe3";// 必须替换public static final String MESSAGE_TEMPLATE_ID = "N6MyyAF0Ucxxxxxxxxxxxxxxxxp-OGsWnQut_niUAaY";/*** 全局AccessToken*/private static AccessToken at;@Overrideprotected void service(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {if ("/wx".equals(request.getServletPath())) {doGetWx(request, response);} else if ("/wx/message".equals(request.getServletPath())) {doSendMessage(request, response);}}/*** @Description 校验配置URL服务器的合法性* @date 2023年5月29日下午4:17:40* @param request* @param response* @throws ServletException* @throws IOException*/public void doGetWx(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {String signature = request.getParameter("signature");String timestamp = request.getParameter("timestamp");String nonce = request.getParameter("nonce");String echostr = request.getParameter("echostr");// 将微信echostr返回给微信服务器try (OutputStream os = response.getOutputStream()) {String sha1 = getSHA1(wxToken, timestamp, nonce, "");// 和signature进行对比if (sha1.equals(signature)) {// 返回echostr给微信os.write(URLEncoder.encode(echostr, "UTF-8").getBytes());os.flush();}} catch (Exception e) {e.printStackTrace();}}/*** @Description 发送模板消息* @date 2023年5月30日上午10:57:45* @param request* @param response* @throws IOException*/private void doSendMessage(HttpServletRequest request, HttpServletResponse response) throws IOException {String token = getToken();String url = "https://api.weixin.qq.com/cgi-bin/user/get?" + "access_token=" + token;// 获取 openid 数组List<String> userOpenids = WXPublicAccountHttpUtil.getOpenidList(url);// 主要的业务逻辑:for (String openId : userOpenids) {TemplateMessage templateMessage = new TemplateMessage();templateMessage.setTouser(openId);templateMessage.settemplate_id(MESSAGE_TEMPLATE_ID);templateMessage.setTopcolor("#FF0000");// key对应创建模板内容中的形参//{{title.DATA}} {{username.DATA}} {{quote.DATA}} {{date.DATA}} // WeChatTemplateMsg对应实参和字体颜色Map<String, WeChatTemplateMsg> data = new HashMap<String, WeChatTemplateMsg>();data.put("title", new WeChatTemplateMsg("你有一条新的消息", "#173177"));data.put("username", new WeChatTemplateMsg("黄先生", "#173177"));data.put("date", new WeChatTemplateMsg("2023年05月29日 16时24分", "#173177"));data.put("quote", new WeChatTemplateMsg("你好", "#173177"));templateMessage.setData(data);System.out.println(templateMessage);WXPublicAccountHttpUtil.sendMessage(getToken(), templateMessage);}}/*** @Description 获取token,本地缓存有就直接返回,没有就发送请求获取(wx官方api获取token每天有限制,因此需做缓存)* @date 2023年5月29日下午4:13:17* @return*/public static String getToken() {if (at == null || at.isExpired()) {getAccessToken();}return at.getAccessToken();}/*** 给AccessToken赋值*/private static void getAccessToken() {// 发送请求获取tokenString token = null;try {String url ="https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential"+ "&appid=" + APPID + "&secret=" + SECRET;token = WXPublicAccountHttpUtil.get(url);} catch (Exception e) {e.printStackTrace();}JSONObject jsonObject = JSONObject.parseObject(token);String accessToken = (String) jsonObject.get("access_token");Integer expiresIn = (Integer) jsonObject.get("expires_in");// 创建token对象,并存储at = new AccessToken(accessToken, String.valueOf(expiresIn));System.out.println(token);}/*** 用SHA1算法生成安全签名** @param token     票据* @param timestamp 时间戳* @param nonce     随机字符串* @param encrypt   密文* @return 安全签名* @throws Exception*/public static String getSHA1(String token, String timestamp, String nonce, String encrypt) throws Exception {try {String[] array = new String[] { token, timestamp, nonce, encrypt };StringBuffer sb = new StringBuffer();// 字符串排序Arrays.sort(array);for (int i = 0; i < 4; i++) {sb.append(array[i]);}String str = sb.toString();// SHA1签名生成MessageDigest md = MessageDigest.getInstance("SHA-1");md.update(str.getBytes());byte[] digest = md.digest();StringBuffer hexstr = new StringBuffer();String shaHex = "";for (int i = 0; i < digest.length; i++) {shaHex = Integer.toHexString(digest[i] & 0xFF);if (shaHex.length() < 2) {hexstr.append(0);}hexstr.append(shaHex);}return hexstr.toString();} catch (Exception e) {e.printStackTrace();}return "";}}

3.4.测试

关注公众号

浏览器访问 http://22717eef.r6.vip.cpolar.cn/xjsrm/wx/message(替换为自己的)

查看微信消息

Java微信公众号发送消息-保姆级教程附源码相关推荐

  1. python训练营微信广告发送机_python实现给微信公众号发送消息的方法

    本文实例讲述了python实现给微信公众号发送消息的方法.分享给大家供大家参考,具体如下: 现在通过发微信公众号信息来做消息通知和告警已经很普遍了.最常见的就是运维通过zabbix调用shell脚本给 ...

  2. 微信公众号怎么推送消息_微信公众号发送消息

    A.模板消息发送 模板消息仅用于公众号向用户发送重要的服务通知,只能用于符合其要求的服务场景中,如信用卡刷卡通知,商品购买成功通知等.不支持广告等营销类消息以及其它所有可能对用户造成骚扰的消息. 备注 ...

  3. 微信公众号对接PHP电影网站,wxapi 微信公众号平台与电影类网站对接源码 wxapi 联合开发网 - pudn.com...

    wxapi 所属分类:微信小程序 开发工具:Java 文件大小:3KB 下载次数:0 上传日期:2019-02-16 23:41:01 上 传 者:lziccard 说明:  微信公众号平台与电影类网 ...

  4. 实现微信公众号发送消息给指定用户

    一.前言 在实际项目开发中,需要实现消息中心向关注微信公众号的指定用户发送消息通知,在翻阅了网上很多资料及微信官方开发文档后,最终顺利完成功能开发,但是其中走过的路艰辛且曲折,因此特将开发过程中踩过的 ...

  5. 手把手教学java微信公众号模版消息开发

    前言:最初接触的时候也是一头雾水,微信的接口文档看也看不懂,最后还是百度了一段时间才出来的结果,觉得网上有些文章还是没有说清除如何简单快捷的做出这个模版,所以我自己整理了一下自己写模版的经验,废话不多 ...

  6. 小程序发送订阅消息,微信公众号发送消息模板

    首先讲两个注意事项 1.小程序和微信公众号的用户openid是不同的. 2.小程序需要用户手动授权订阅消息通知(一次性订阅是订阅一次发一次,长期订阅可以多发). 关于小程序和公众号AppID和AppS ...

  7. 微信公众号发送消息通知

    微信申请测试公众号 申请测试号 微信公众平台接口调试工具 接口调试工具 如何获取用户的openid 获取用户的openid 4.通过微信公众号后台聊天获取用户的openid 测试平台获取appID,a ...

  8. 该微信用户未开启“公众号安全助手”的消息接收功能,请先开启后再绑定,Java微信公众号开发消息推送公众号用户绑定问题 的解决办法

    问题概述 在进行微信公众号开发的时候遇到的这个问题,通过Web开发公众号的模板消息推送,在调试的过程中,需要进行开发者接口联调&调试,在调试之前需要将当前的公众号与用户的微信号进行绑定, 绑定 ...

  9. Java微信公众号开发之创建带参数二维码

    生成带参数二维码接口文档:生成带参数二维码 一.介绍 1.1.目前有2种类型的二维码: 1. 临时二维码:临时二维码,是有过期时间的,最长可以设置为在二维码生成后的30天(即2592000秒)后过期, ...

最新文章

  1. Mysql学习笔记【原创】
  2. 在quartus 和 ISE 里直接调用modelsim的方法(转)
  3. css实现文字在横线上居中
  4. 151. Leetcode 剑指 Offer 14- I. 剪绳子 (贪心算法-基础题目)
  5. mysql还原数据mysqldump
  6. 如何从XMLHttpRequest创建自定义获取API
  7. python 第三方模块之 pandas 操作 excel
  8. phpcms文件夹plugin调用怎么写路径 - 代码篇
  9. mysql and 和where_如何使用mysql查询where条件里的or和and
  10. Spring 事务使用详解
  11. 文本分类训练集 测试集_【AI1000问】训练为什么要分测试集和验证集?
  12. 如何让机器像人一样多角度思考?
  13. DeNA/上海纵游通过使用AWS大量缩短新款游戏和服务的上线时间
  14. mysql queries 很大,mysql优化通常使用的几种方法
  15. 最经典的PS入门笔记,来自李涛老师的高手之路
  16. css+js显示点阵字体/LED七段数码管字体(模拟)
  17. network 节点label以及相关字体设置
  18. ckplayer.js视频播放插件
  19. 短文本相似度计算-simHash从原理到实现
  20. kubernetes 源码安装1.18.3 (2)部署etcd集群

热门文章

  1. 【软件周刊第 25 期】深度操作系统 15.4 正式发布;CentOS 7 重要 Linux Kernel 安全更新发布...
  2. linux查看网络链路,linux网络链路测试
  3. 基于GRASP原则的设计模式
  4. [Vim] Vim在tmux中颜色改变/不同的问题
  5. 【面试题系列|Java】Java基础面试题
  6. Ubuntu安装ssh-server一种成功示例
  7. python中turtle画老虎_通过Turtle库在Python中绘制一个鼠年福鼠
  8. 深度学习在电力系统的应用方向
  9. 2022年智慧医疗行业研究报告
  10. oracle partition by list,深入解析partition-list 分区