JAVA对接微信公众号(服务号、订阅号)实现模板消息推送功能

最近做了个需求要求对接微信公众号,使用模板进行消息推送,今天抽时间总结一下相关的逻辑。

大概逻辑分为四个步骤:

  1. 获取微信调用接口凭证access_token。
  2. 获取微信模板列表。
  3. 使用模板进行消息推送。
  4. 公众号配置服务器URL。

我这里的实现没有使用微信公众号weixin-java-mp依赖,完全是根据微信官方文档的提示写的代码。

一、二、获取access_token和模板列表

    @Override
    public String getAccessToken() {
        RestTemplate restTemplate = new RestTemplate();
        String accessTokenUrl = getAccessTokenUrl + "?grant_type=" + grantType + "&appid=" + appId + "&secret=" + secret;
        ResponseEntity<JSONObject> responseEntity = restTemplate.getForEntity(accessTokenUrl, JSONObject.class);
        if (responseEntity.getStatusCodeValue() == 200) {
            JSONObject result = responseEntity.getBody();
            if (result != null) {
                int errCode = result.getIntValue("errcode");
                if (errCode == 0) {
                    String accessToken = result.getString("access_token");
                    redisTemplate.opsForValue().set(WeChatConstant.WECHAT_ACCESS_TOKEN_REDIS_KEY, accessToken, 7200, TimeUnit.SECONDS);
                    return accessToken;
                } else {
                    String errMsg = result.getString("errmsg");
                    logger.info("获取微信access_token返回失败,错误码:{},错误信息:{}", errCode, errMsg);
                }
            } else {
                logger.info("获取微信返回结果json为空");
            }
        } else {
            logger.info("调用微信接口请求失败");
        }
        return null;
    }

    @Override
    public List<MsgTemplateVO> getAllTemplates() {
        RestTemplate restTemplate = new RestTemplate();
        String accessToken = redisTemplate.opsForValue().get(WeChatConstant.WECHAT_ACCESS_TOKEN_REDIS_KEY);
        if (StringUtils.isBlank(accessToken)) {
            accessToken = this.getAccessToken();
        }
        String url = getAllTemplateUrl + "?access_token=" + accessToken;
        List<MsgTemplateVO> templateVOList = new ArrayList<>();
        ResponseEntity<JSONObject> responseEntity = restTemplate.getForEntity(url, JSONObject.class);
        if (responseEntity.getStatusCodeValue() == 200) {
            JSONObject result = responseEntity.getBody();
            if (result != null) {
                JSONArray array = result.getJSONArray("template_list");
                if (!array.isEmpty()) {
                    for (int i = 0; i < array.size(); i++) {
                        JSONObject template = array.getJSONObject(i);
                        MsgTemplateVO msgTemplateVO = new MsgTemplateVO();
                        msgTemplateVO.setTemplateId(template.getString("template_id"));
                        msgTemplateVO.setTitle(template.getString("title"));
                        msgTemplateVO.setPrimaryIndustry(template.getString("primary_industry"));
                        msgTemplateVO.setPrimaryIndustry(template.getString("deputy_industry"));
                        msgTemplateVO.setContent("content");
                        msgTemplateVO.setContent("example");
                        templateVOList.add(msgTemplateVO);
                    }
                }
            } else {
                logger.info("获取微信模板返回结果json为空");
            }
        } else {
            logger.info("调用微信模板接口请求失败");
        }
        return templateVOList;
    }

说明:MsgTeplateVO是我自己定义的消息模板类。

properties配置请求接口的常量

wechat.auth.granttype=client_credential
wechat.auth.token=token
wechat.auth.appid=服务号的appId
wechat.auth.secret=服务号的secret
#获取AccessToken url GET
wechat.url.accesstoken=https://api.weixin.qq.com/cgi-bin/token
#模板消息推送 POST
wechat.url.template.send=https://api.weixin.qq.com/cgi-bin/message/template/send
#获取所有的消息模板 GET
wechat.url.get.all.template=https://api.weixin.qq.com/cgi-bin/template/get_all_private_template

 

三、使用模板进行消息推送主要代码。参数设置,发送请求,获取结果

//send url
String sendUrl = templateSendUrl + "?access_token=" + accessToken;
JSONObject params = new JSONObject();
params.put("touser", "openid");
params.put("template_id", msgTemplateVO.getTemplateId());
//模板参数
params.put("data", contentJson);
//header
HttpHeaders header = new HttpHeaders();
header.setContentType(MediaType.APPLICATION_JSON);
//HttpEntity
HttpEntity<JSONObject> httpEntity = new HttpEntity<>(params, header);
RestTemplate restTemplate = new RestTemplate();
//JSONObject数据结果
JSONObject result = restTemplate.postForObject(sendUrl, httpEntity, JSONObject.class);
//返回结果
List<MsgMiddleResult> results = msgRecordVO.getResults();
if (result != null) {
	Integer errCode = result.getInteger("errcode");
	if (errCode == 0) {
		//成功
		
	} else {
		//失败
		String errMsg = result.getString("errmsg");
		
	}
}
自己使用测试号,定义了模板,写了测试类进行测试如下:

四、公众号配置服务器URL

@GetMapping("/checkSignature")
public String checkSignature(HttpServletRequest request) {
	String signature = request.getParameter("signature");
	String timestamp = request.getParameter("timestamp");
	String nonce = request.getParameter("nonce");
	String echostr = request.getParameter("echostr");

	logger.info("接收来自微信服务器的认证信息。signature={},timestamp={},nonce={},echostr={}",
			signature, timestamp, nonce, echostr);
	if(StringUtils.isAnyBlank(signature,timestamp,nonce,echostr)){
		logger.info("请求参数非法");
		return null;
	}
	//加密后的mySignature与微信公众平台的signature一致
	boolean check = SignUtil.checkSignature(token, signature, timestamp, nonce);
	if (check) {
		return echostr;
	}
	return null;
}
 /**
     * 校验签名
     *
     * @param token     服务器配置里写的TOKEN
     * @param signature 签名
     * @param timestamp 时间戳
     * @param nonce     随机数
     * @return true 成功,false 失败
     */
    public static boolean checkSignature(String token, String signature, String timestamp, String nonce) {

        String checkText = null;
        if (null != signature) {
            //对Token,timestamp nonce 按字典排序
            String[] paramArr = new String[]{token, timestamp, nonce};
            Arrays.sort(paramArr);
            //将排序后的结果拼成一个字符串
            String content = paramArr[0].concat(paramArr[1]).concat(paramArr[2]);
            try {
                MessageDigest md = MessageDigest.getInstance("SHA-1");
                //对接后的字符串进行sha1加密
                byte[] digest = md.digest(content.getBytes());
                checkText = byteToStr(digest);
            } catch (NoSuchAlgorithmException e) {
                e.printStackTrace();
            }
        }
        //将加密后的字符串与signature进行对比
        return checkText != null && checkText.equals(signature.toUpperCase());
    }

    /**
     * 将字节数组转化为16进制字符串
     *
     * @param byteArrays 字符数组
     * @return 字符串
     */
    private static String byteToStr(byte[] byteArrays) {
        StringBuilder str = new StringBuilder();
        for (byte byteArray : byteArrays) {
            str.append(byteToHexStr(byteArray));
        }
        return str.toString();
    }

    /**
     * 将字节转化为十六进制字符串
     *
     * @param myByte 字节
     * @return 字符串
     */
    private static String byteToHexStr(byte myByte) {

        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[(myByte >>> 4) & 0X0F];
        tempArr[1] = digit[myByte & 0x0F];
        return new String(tempArr);
    }

我这里是测试接口用了ngrok。具体的使用方法可以参考这篇文章https://www.jianshu.com/p/571fdbc98d25

注:在验证token的时候,我是写在配置文件里了,如果使用文章开始的jar包。可以通过appId直接获取到。

  • 4
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值