最近因为业务需要使用微信服务号推送模板消息,所以就研究了下,在这也回顾和总结下开发流程和注意事项。
1、 微信公众号推送模板消息,需要开通服务号并且需要进行微信认证(这点就不过多记录了)。申请到服务号后在公众号—>开发—>基本配置中会得到服务号唯一标识(appid)与服务号的秘钥(AppSecret)。
2、有了appid后就需要进行服务器配置
这里填写的URL就是后台访问地址,微信会通过此URL发送一个GET请求,请求参数如下:
后台方法中通过检验signature对请求进行校验,若确认此次GET请求来自微信服务器,原样返回echostr参数内容,则接入生效,否则接入失败。加密/校验流程如下: - 将token、timestamp、nonce三个参数进行字典排序 (注:此处的token为配置信息中填写的token) - 将三个参数字符串拼接成一个字符串进行sha1加密 - 开发者获得加密后的字符串可与signature对比,标识该请求来源于微信 配置好URL后下面的token和EncodingAESKey可随机填写,这里的token就是上面URL中检验时用到的token。最后一个就是消息加密方式,这里因为是初步开发所以选择的是明文,就是对参数没有进行加密以便开发和调试,后面需要的话可以选择其它两种方式但再接收和返回时需要进行相对应的加密和解密操作。 提交前需要在基础配置中的IP白名单中添加GET请求服务器的IP才能通过。
验证请求代码如下:
Controller()
/**
* 微信服务号绑定验证接口
* @param signature
* @param timestamp
* @param nonce
* @param echostr
* @return
*/
@GetMapping(value = "/verification/interface")
public String wechatVerificationInterface(String signature, String timestamp, String nonce, String echostr) {
logger.info("wechatVerificationInterface 接收消息验证接");
logger.info("微信加密签名signature:" + signature);
logger.info("时间戳timestamp:" + timestamp);
logger.info("随机数nonce:" + nonce);
logger.info("随机字符串echostr:" + echostr);
//验证微信签名,判断是否来自配置微信端
if (AppletsSignUtil.checkSignature(signature, timestamp, nonce)){
return echostr;
}
return null;
}
验证签名工具类
/**
* @Description: 请求校验工具类
* @Author caiyu
* @Date 2021/5/22 14:39
*/
public class AppletsSignUtil {
// 与开发模式接口配置信息中的Token保持一致.
private static String token = "leyizhuang";
/**
* 校验签名
* @param signature 微信加密签名.
* @param timestamp 时间戳.
* @param nonce 随机数.
* @return
*/
public static boolean checkSignature(String signature, String timestamp, String nonce) {
// 对token、timestamp、和nonce按字典排序.
String[] paramArr = new String[] {token, timestamp, nonce};
Arrays.sort(paramArr);
// 将排序后的结果拼接成一个字符串.
String content = paramArr[0].concat(paramArr[1]).concat(paramArr[2]);
String ciphertext = null;
try {
MessageDigest md = MessageDigest.getInstance("SHA-1");
// 对拼接后的字符串进行sha1加密.
byte[] digest = md.digest(content.toString().getBytes());
ciphertext = byteToStr(digest);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
// 将sha1加密后的字符串与signature进行对比.
return ciphertext != null ? ciphertext.equals(signature.toUpperCase()) : false;
}
/**
* 将字节数组转换为十六进制字符串.
* @param byteArray
* @return
*/
private static String byteToStr(byte[] byteArray) {
String strDigest = "";
for (int i = 0; i < byteArray.length; i++) {
strDigest += byteToHexStr(byteArray[i]);
}
return strDigest;
}
/**
* 将字节转换为十六进制字符串.
* @param mByte
* @return
*/
private static String byteToHexStr(byte mByte) {
char[] Digit = { '0', '1' , '2', '3', '4' , '5', '6', '7' , '8', '9', 'A' , 'B', 'C', 'D' , 'E', 'F'};
char[] tempArr = new char[2];
tempArr[0] = Digit[(mByte >>> 4) & 0X0F];
tempArr[1] = Digit[mByte & 0X0F];
String s = new String(tempArr);
return s;
}
}
3、配置成功后,接下来是选择需要发送的模板,之前说过需要发送模板消息是必须要微信认证的。认证完成后可以在功能—>添加功能插件—>模板消息进行添加
添加好功能后就可以根据提示选择需要的模板类型以及填写申请理由,然后进行模板选择(目前最多可以选择25个模板),选择好每个模板都会有一个模板ID,此ID在推送消息时用来指定模板
4、有了模板ID后,就可以从后台推送此模板消息了。
推送模板消息代码如下:
/***
* 发送模板消息
* (消费成功通知)
*/
public void sendCreateOrderTemplateMsg() throws UnsupportedEncodingException,MissingInformationException {
private static final String SEND_MSG_API = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=";
//获取token
String token = this.getToken();
//消息模板ID
String template_id = XXXXXXXX;
// 接口地址
String sendMsgApi = SEND_MSG_API + token;
//openId微信号关注服务号的唯一标识
String toUser = xxxxxx;
//整体参数map
Map<String, Object> paramMap = new HashMap<String, Object>();
//消息主题显示相关map
Map<String, Object> dataMap = new HashMap<String, Object>();
//根据自己的模板定义内容和颜色
dataMap.put("first", new AppletsDO("尊敬的顾客,最新交易提醒:", "#173177"));
dataMap.put("time", new AppletsDO("2021-06-17 14:00:51", "#173177"));
dataMap.put("org", new AppletsDO("富森富之美", "#173177"));
dataMap.put("type", new AppletsDO("订单消费", "#173177"));
dataMap.put("money", new AppletsDO("8163.0", "#173177"));
dataMap.put("point", new AppletsDO("0", "#173177"));
dataMap.put("remark", new AppletsDO("积分需在订单结清并且已出货后再进行统计一并增
加到您的积分账户中!", "#173177"));
paramMap.put("touser", toUser);
paramMap.put("template_id", template_id);
paramMap.put("data", dataMap);
//需要实现跳转网页的,可以添加下面一行代码实现跳转
// paramMap.put("url","");
System.out.println(this.doGetPost(sendMsgApi, "POST", paramMap));
}
/**
* 获取token
*
* @return token
*/
@Override
public String getToken() {
// 授予形式
String grant_type = "client_credential";
// 接口地址拼接参数(appid为微信服务号的appid,secret为服务号的秘钥)
String getTokenApi = "https://api.weixin.qq.com/cgi-bin/token?grant_type=" + grant_type + "&appid=" + XXXXXX
+ "&secret=" + XXXXXX;
String tokenJsonStr = this.doGetPost(getTokenApi, "GET", null);
JSONObject tokenJson = JSONObject.parseObject(tokenJsonStr);
String token = tokenJson.get("access_token").toString();
System.out.println("获取到的TOKEN : " + token);
return token;
}
/**
* 调用接口 post
* @param apiPath
*/
public static String doGetPost(String apiPath,String type,Map<String,Object> paramMap){
OutputStreamWriter out = null;
InputStream is = null;
String result = null;
try{
URL url = new URL(apiPath);// 创建连接
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setDoOutput(true);
connection.setDoInput(true);
connection.setUseCaches(false);
connection.setInstanceFollowRedirects(true);
connection.setRequestMethod(type) ; // 设置请求方式
connection.setRequestProperty("Accept", "application/json"); // 设置接收数据的格式
connection.setRequestProperty("Content-Type", "application/json"); // 设置发送数据的格式
connection.connect();
if(type.equals("POST")){
out = new OutputStreamWriter(connection.getOutputStream(), "UTF-8"); // utf-8编码
out.append(JSON.toJSONString(paramMap));
out.flush();
out.close();
}
// 读取响应
is = connection.getInputStream();
int length = (int) connection.getContentLength();// 获取长度
if (length != -1) {
byte[] data = new byte[length];
byte[] temp = new byte[512];
int readLen = 0;
int destPos = 0;
while ((readLen = is.read(temp)) > 0) {
System.arraycopy(temp, 0, data, destPos, readLen);
destPos += readLen;
}
result = new String(data, "UTF-8"); // utf-8编码
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return result;
}
最终效果如下:
至此一个简单的模板推送就算完成了,这个只是搭建好了发送通道,实际中我们要根据不同业务场景发送不同的模板,最重要的还要获取到每个关注者的openid才能对其发送消息。之后再来说说本人获取openid踩的坑。如有大神路过欢迎指点迷津