【java】java消息推送至微信公众号详细教程

news2024/11/25 22:48:16

文章目录

  • 读前必看
  • 测试号推送

谁说程序员不懂浪漫? 将的关心 推送至微信公众号 给女朋友及时的关怀~(这位同学 你女朋友呢?)

读前必看

关于微信开发平台,小程序和公众号是不一样的,而公众号又会区分订阅号、服务号、测试号,服务号会比订阅号拥有更多权限,而订阅号又区分是否个人主体,很不幸博主一开始在捣鼓的是个人主体的订阅号,试了半天,然后看到了文档 发现这类公众号什么权限都没有:
在这里插入图片描述
在这里插入图片描述


干得漂亮 … 当然 如果不是个人主体的订阅号 或者是服务号的同学,是可以在这页面继续认证的
在这里插入图片描述

那既然这个什么都干不了,我们可以转战测试号,测试号有个很明显的弊端就是公众号名称很难看 还改不了。。。
当然 相信同学们的对象(new Object() ) 是不会嫌弃的

本文主要介绍测试号如何推送,当然 如果你的订阅号不是个人主体 也可以参考一下 基本是差不多的,但如果是个人主体,调用没权限的接口就会报48001。

测试号推送

  1. 首先我们在微信公众号平台 https://mp.weixin.qq.com/ 找到开发者工具
    选择公众平台测试账号
    在这里插入图片描述

这里需要注意的是 URL需要公网,如果没有服务器域名,我们可以使用免费的内网穿透软件 博主这里使用的是natapp,natapp可以将本地ip端口映射到公网域名

如下图 http://xxxx.xxfree.cc 其实就是127.0.0.1:8080(项目地址)映射的

/wx 是我们项目中的路径 也就是@RequestMapping(“/wx”)
在这里插入图片描述

在提交前 我们需要服务端已经写好了接口 用于校验URL 代码如下:

 
import org.apache.commons.io.IOUtils;
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.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;

@Controller
@RequestMapping("/wx")
public class WeChatUrlController {

 

    /**
     * 用于微信公众号平台URL校验
     * @param req
     * @param response
     * @throws Exception
     */
    @GetMapping
    public void weChatConnect(HttpServletRequest req, HttpServletResponse response) throws Exception {
        Signature sg = new Signature(
                req.getParameter("signature"),
                req.getParameter("timestamp"),
                req.getParameter("nonce"),
                req.getParameter("echostr"));

        ServletInputStream inputStream = req.getInputStream();
        String s = IOUtils.toString(inputStream, "UTF-8");
        System.out.println(s);
        String method = req.getMethod();
        // 如果是微信发过来的GET请求
        if ("GET".equals(method)) {
            if (CheckUtil.checkSignature(sg)) {
                System.out.println("微信连接成功!");
                response.setCharacterEncoding("UTF-8");
                response.getWriter().write(sg.getEchostr());
            }
        }

    }

   
}


public class Signature {
    private String signature;
    private String timestamp;
    private String nonce;
    private String echostr;

    public Signature() {
        super();
        // TODO Auto-generated constructor stub
    }

    public Signature(String signature, String timestamp, String nonce, String echostr) {
        super();
        this.signature = signature;
        this.timestamp = timestamp;
        this.nonce = nonce;
        this.echostr = echostr;
    }

    public String getSignature() {
        return signature;
    }

    public void setSignature(String signature) {
        this.signature = signature;
    }

    public String getTimestamp() {
        return timestamp;
    }

    public void setTimestamp(String timestamp) {
        this.timestamp = timestamp;
    }

    public String getNonce() {
        return nonce;
    }

    public void setNonce(String nonce) {
        this.nonce = nonce;
    }

    public String getEchostr() {
        return echostr;
    }

    public void setEchostr(String echostr) {
        this.echostr = echostr;
    }

    @Override
    public String toString() {
        return "Signature [signature=" + signature + ", timestamp=" + timestamp + ", nonce=" + nonce + ", echostr="
                + echostr + "]";
    }
}


import java.security.MessageDigest;
import java.util.Arrays;

public class CheckUtil {

    private static final String token = "ismengqiutoken";

    public static boolean checkSignature(Signature sg) {

        String[] arr = new String[] { token, sg.getTimestamp(), sg.getNonce() };
        // 排序
        Arrays.sort(arr);
        // 生成字符串
        StringBuffer content = new StringBuffer();
        for (int i = 0; i < arr.length; i++) {
            content.append(arr[i]);
        }

        // sha1加密
        String temp = getSha1(content.toString());
        // 比较
        return temp.equals(sg.getSignature());
    }

    // 加密算法
    public static String getSha1(String str) {
        if (str == null || str.length() == 0) {
            return null;
        }

        char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
        try {
            MessageDigest mdTemp = MessageDigest.getInstance("SHA1");
            mdTemp.update(str.getBytes("UTF-8"));
            byte[] md = mdTemp.digest();
            int j = md.length;
            char buf[] = new char[j * 2];
            int k = 0;

            for (int i = 0; i < j; i++) {
                byte byte0 = md[i];
                buf[k++] = hexDigits[byte0 >>> 4 & 0xf];
                buf[k++] = hexDigits[byte0 & 0xf];
            }

            return new String(buf);
        } catch (Exception e) {
            return null;
        }

    }
}
  1. 提交成功后 第二步就是获取用户的openId了

    我们可以引入一个starter 大幅度简化代码 不用再手动去调用url了

            <dependency>
                <groupId>com.github.binarywang</groupId>
                <artifactId>wx-java-mp-spring-boot-starter</artifactId>
                <version>4.0.0</version>
            </dependency>
    

    获取openId的方法 我们同样写在WeChatUrlController, 完整代码变为如下:


import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.api.WxMpUserService;
import me.chanjar.weixin.mp.bean.result.WxMpUserList;
import org.apache.commons.io.IOUtils;
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.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;

@Controller
@RequestMapping("/wx")
public class WeChatUrlController {

    @Autowired
    private WxMpService wxMpService;

    /**
     * 用于微信公众号平台URL校验
     * @param req
     * @param response
     * @throws Exception
     */
    @GetMapping
    public void weChatConnect(HttpServletRequest req, HttpServletResponse response) throws Exception {
        Signature sg = new Signature(
                req.getParameter("signature"),
                req.getParameter("timestamp"),
                req.getParameter("nonce"),
                req.getParameter("echostr"));

        ServletInputStream inputStream = req.getInputStream();
        String s = IOUtils.toString(inputStream, "UTF-8");
        System.out.println(s);
        String method = req.getMethod();
        // 如果是微信发过来的GET请求
        if ("GET".equals(method)) {
            if (CheckUtil.checkSignature(sg)) {
                System.out.println("微信连接成功!");
                response.setCharacterEncoding("UTF-8");
                response.getWriter().write(sg.getEchostr());
            }
        }

    }

    /**
     * 获取用户的 openId
     * @param req
     * @param response
     * @throws Exception
     */
    @PostMapping
    public void getUserInfo(HttpServletRequest req, HttpServletResponse response) throws Exception {

        // 方式一 获取所有关注该公众号的用户的openId
        WxMpUserService userService = wxMpService.getUserService();
        WxMpUserList wxMpUserList = userService.userList("");
        List<String> openIds = wxMpUserList.getOpenids();
        System.out.println(openIds);

        // 方式二  让用户在微信公众号发消息 会进入此接口 (POST请求  和我们设置的URL /wx 对应)
        ServletInputStream inputStream = req.getInputStream();
        String s = IOUtils.toString(inputStream, "UTF-8");
        // 打印结果中的 FromUserName节点 <FromUserName><![CDATA[x-xxxx]]></FromUserName>
        // x-xxxx就是openId了 
        System.out.println(s);

    }

}


  1. 设置消息模板 在设置URL的同个页面 往下翻 复制模板id
    在这里插入图片描述


import com.alibaba.fastjson.JSONObject;
import com.qiuhuanhen.sendforyou.entity.WeChatTemplateMsg;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.client.RestTemplate;

import javax.annotation.PostConstruct;
import java.util.HashMap;
import java.util.Map;

@Slf4j
@Component
public class Message {

    @PostConstruct
    @GetMapping("/sendMessage")
    public  String sendMessage() {
        // 模板参数
        Map<String, WeChatTemplateMsg> sendMag = new HashMap<String, WeChatTemplateMsg>();
		// 需要替换成你自己的三个参数
        // openId代表一个唯一微信用户,即微信消息的接收人
        String openId = "x-xxx";
        // 消息模板id
        String templateId = "xxxx";
        // 微信的基础accessToken
        String accessToken =
                "x-xx-xxx";
        String url = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=" + accessToken;

      	/**
      	 *  key和模板id相对应 这里取了两个key做测试 
      	 **/
        sendMag.put("temperatureDay", new WeChatTemplateMsg("111"));
        sendMag.put("weatherDay", new WeChatTemplateMsg("333"));
        RestTemplate restTemplate = new RestTemplate();
        //拼接base参数
        Map<String, Object> sendBody = new HashMap<>();
        sendBody.put("touser", openId);               // openId
        sendBody.put("url", "www.baidu.com");         // 点击模板信息跳转地址
        sendBody.put("topcolor", "#FF0000");          // 顶色
        sendBody.put("data", sendMag);                   // 模板参数
        sendBody.put("template_id", templateId );      // 模板Id
        ResponseEntity<String> forEntity = restTemplate.postForEntity(url, sendBody, String.class);
        log.info("结果是: {}",forEntity.getBody());
        JSONObject jsonObject = JSONObject.parseObject(forEntity.getBody());
        // 0
        String messageCode = jsonObject.getString("errcode");
        // 2431260672639467520
        String msgId = jsonObject.getString("msgid");
        System.out.println("messageCode : " + messageCode + ", msgId: " +msgId);
        return forEntity.getBody();
    }
}




import lombok.*;

@Data
@EqualsAndHashCode
@AllArgsConstructor
@NoArgsConstructor
public class WeChatTemplateMsg {
    /** msg **/
    private String value;
    private String color;

    public WeChatTemplateMsg(String value) {
        this.value = value;
    }
}


  /**
     * 获取接口访问凭证
     *
     * @param appid 凭证
     * @param appsecret 密钥
     * @return
     */
    public static AccessToken getToken(String appid, String appsecret) {
        AccessToken token = null;
        String requestUrl = tokenUrl.replace("APPID", appid).replace("APPSECRET", appsecret);
        // 发起GET请求获取凭证
        JSONObject jsonObject = httpsRequest(requestUrl, "GET", null);

        if (null != jsonObject) {
            try {
                token = new AccessToken();
                token.setAccessToken(jsonObject.getString("access_token"));
                token.setExpiresIn(jsonObject.getInteger("expires_in"));
            } catch (JSONException e) {
                token = null;
                // 获取token失败
                log.error("获取token失败 errcode:{} errmsg:{}", jsonObject.getInteger("errcode"), jsonObject.getString("errmsg"));
            }
        }
        return token;
    }

简易效果图:
在这里插入图片描述

我们的推送内容 可以是天气预报等等,天气预报推荐用高德开放平台,步骤也比较简单。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/385117.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

西湖论剑 2022复现

目录 <1> [西湖论剑 2022] Node Magical Login <2> [西湖论剑 2022] real_ez_node(ejs原型链污染&http拆分攻击) <3> [西湖论剑 2022] 扭转乾坤 (RFC差异绕过header头内容限制) <4> [西湖论剑 2022] unusual php(IDA拿rce密钥&利用rce密钥…

DSS 部署环境需求清单

文章目录 DSS系统需求项目地址计算资源计算基准:计算引擎程序硬件需求表 :DSS计算及存储资源需求计算资源计算基准:计算程序硬件需求表:DSS系统需求 项目地址 https://github.com/WeBankFinTech/DataSphereStudio 计算资源计算基准: 1.日活用户10万。 2.单用户单日总…

网络安全怎么学?20年白帽子老江湖告诉你

很多人都知道龙叔是个老程序员&#xff0c;但却不知道其实我也是个H客&#xff0c;20年前我就开始痴迷于H客技术&#xff0c;可以说是网络安全方面的老江湖了。 到现在&#xff0c;我还依然会去研究这一块&#xff0c;偶尔会和一些网安的朋友交流技术&#xff0c;比如说红盟的…

Nginx的三大特点

作为一个 Web 服务器&#xff0c;Nginx 的功能非常完善&#xff0c;完美支持 HTTP/1、HTTPS 和 HTTP/2&#xff0c;而且还在不断进步。 1、进程池 Nginx 作为“轻量级”的服务器&#xff0c;它的 CPU、内存占用都非常少&#xff0c;同样的资源配置下就能够为更多的用户提供服…

Android 绘图基础:Canvas画布——自定义View基础(绘制表盘、矩形、圆形、弧、渐变)

Canvas画布&#xff0c;通过它我们可以自定义一个View&#xff0c;设置View的相关效果之类的。感觉用法差不多&#xff0c;重要的是要理解方法中传入的参数的含义&#xff0c;比如float类型的参数&#xff0c;传递的是坐标,已开是没有注意传入的参数时坐标&#xff0c;导致我迷…

windows编译x265

msys2的安装参考&#xff1a; MSYS2安装_蜡笔小方的博客-CSDN博客 将下载好的x265放入msys2能找到的目录下&#xff1a; 打开msys2&#xff0c;切换到x265/build/msys-cl目录下 其中&#xff0c;build目录下是x265为了各种平台或者不同编译工具配的编译脚本&#xff1f;&…

儿童护目台灯哪种好用?几款真的保护视力的台灯品牌推荐

儿童眼睛还未发育完全&#xff0c;眼睛比较脆弱&#xff0c;但是现在的小孩子学习任务也比较繁重&#xff0c;经常晚上看书写字&#xff0c;所以选择合适的护眼台灯来保护眼睛很重要。 选择儿童护目台灯需要注意以下几个方面&#xff1a; &#xff08;一&#xff09;色温和亮…

图解KMP算法

子串的定位操作通常称作串的模式匹配。你可以理解为在一篇英语文章中查找某个单词是否存在&#xff0c;或者说在一个主串中寻找某子串是否存在。朴素的模式匹配算法假设我们要从下面的主串S "goodgoogle" 中&#xff0c;找到T "google" 这个子串的位置。…

“终于我从腾讯离职了...”一个年薪40W的测试工程师的自白....

”我递上了我的辞职信&#xff0c;不是因为公司给的不多&#xff0c;也不是因为公司待我不好&#xff0c;但是我觉得&#xff0c;我每天看着我憔悴的面容&#xff0c;每天晚上拖着疲惫的身体躺在床上&#xff0c;我都不知道人生的意义&#xff0c;是赚钱吗&#xff1f;是为了更…

哪种蓝牙耳机通话效果好?通话清晰的蓝牙耳机推荐

出门的时候&#xff0c;如果戴耳机和别人通话&#xff0c;就不必把耳机摘下来&#xff0c;接电话变得前所未有的简单。现在的蓝牙耳机&#xff0c;已经不是单纯的用来听音乐了&#xff0c;而是一种更好的功能。下面这四款蓝牙耳机不仅适合听歌&#xff0c;通话还清晰&#xff0…

C++---最长上升子序列模型---最大上升子序列和(每日一道算法2023.3.3)

注意事项&#xff1a; 本题为"线性dp—最长上升子序列的长度"的扩展题&#xff0c;所以dp思路这里就不再赘述。 题目&#xff1a; 比如&#xff0c;对于序列(1,7,3,5,9,4,8)&#xff0c;有它的一些上升子序列&#xff0c;如(1,7),(3,4,8)等。 这些子序列中和最大为…

【Web安全】PHP安全

一、文件包含漏洞严格来说&#xff0c;文件包含就是代码注入的一种。代码注入&#xff0c;其原理就是注入一段用户能控制的脚本或代码并让服务器端执行。代码注入的典型代表就是文件包含。文件包含可能会出现在JSP、PHP、ASP等语言中&#xff0c;常见函数如下&#xff1a;PHP&a…

好记又实用的获取电脑型号方法

个人常用的方法 方法二最好记又好用。 方法一 dxdiag命令 按下键盘WINR调出运行在输入框输入dxdiag命令后&#xff0c;按下回车&#xff1b;进入DirectX诊断工具&#xff0c;便可查看系统型号等信息。 这里就会显示系统型号。 方法二 设备和打印机 控制面板-查看方式-小图…

Kubernetes学习(二)Pod

创建Pod kubectl创建nginx pod 编写 nginx pod的yaml文件 apiVersion: v1 kind: Pod metadata:name: my-nginxlabels:name: my-nginx spec:containers:- image: nginxname: my-nginxresources:limits:memory: "128Mi"cpu: "500m"ports:- name: nginx-po…

周期性温度和压力波的PID自动控制解决方法

摘要&#xff1a;目前各种PID控制器仪表常用于简单的设定点&#xff08;Set Point&#xff09;和斜坡&#xff08;Ramp&#xff09;程序控制&#xff0c;但对于复杂的正弦波等周期性变量的控制则无能为力。为了采用标准PID控制器便捷和低成本的实现对正弦波等周期性变量的自动控…

TiDB Serverless 和技术生态全景

数据库不是单一软件&#xff0c;而是一个生态体系。成为一款好用的数据库&#xff0c;除了产品自身的能力外&#xff0c;繁荣的技术生态体系也至关重要&#xff0c;既可以提升使用体验&#xff0c;又可以降低使用门槛。 PingCAP 在 2022 年 11 月 1 日正式发布了 TiDB Cloud Se…

浏览器的渲染过程解析

文章目录浏览器渲染进程有哪些&#xff1f;浏览器的渲染过程浏览器渲染进程有哪些&#xff1f; GUI线程&#xff1a;负责渲染浏览器页面&#xff0c;解析html&#xff0c;css&#xff0c;构建DOM树&#xff0c;CSS规则树&#xff0c;渲染树和绘制页面&#xff0c;当界面需要重…

Unity使用webSocket与服务器通信(一)搭建一个简单地服务器和客户端

你想在unity WebGL里面使用TCP通信吗&#xff0c;那么你可以用一用webSocket。当然&#xff0c;桌面端也可以使用webSocket&#xff0c;这样Unity多平台发布的时候&#xff0c;业务层的通信代码可以使用一套&#xff0c;而不是桌面用socket&#xff0c;网页用http… 一、什么是…

精选博客系列|VMware 帮助服务提供商构建零碳未来

当谈到可持续发展的未来时&#xff0c;你首先想到的是哪些技术&#xff1f;大多数人会说电动汽车、太阳能电池板或风电场。但是&#xff0c;我们越来越需要将另一种技术放在列表的首位&#xff1a;通信网络。 领先的通信服务提供商 (CSP) 越来越多地致力于环境可持续性的前沿。…

IDCF DevOps黑客马拉松挑战赛(北京 上海 广州 杭州 大连)

IDCF DevOps黑客马拉松&#xff1a;“打造端到端的DevOps人才”&#xff01; “在36小时内从0到1打造并发布一款产品&#xff01;” DevOps实践与敏捷开发正在席卷IT公司&#xff0c;精益创业、设计思维、增长黑客等硅谷创新思维与方法&#xff0c;正在被越来越多具有真知灼见…