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

这篇具有很好参考价值的文章主要介绍了Java微信公众号发送消息-保姆级教程附源码。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

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就完事了,需要后端接口配合校验。

界面:java微信公众号开发源码,微信

 测试号界面:java微信公众号开发源码,微信

 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 {
	
    // 服务器配置填写的token
    private static final String wxToken = "888888";

    @Override
	protected 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可以吗?答案是不可以!

java微信公众号开发源码,微信

 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填到对应的配置栏中点击提交即可。java微信公众号开发源码,微信

 3.1.5. 可能存在的问题

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

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

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

         ngrok错误排查示例:         java微信公众号开发源码,微信

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}}结尾(具体传参看后续代码)

java微信公众号开发源码,微信

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;
		}

		@Override
		public 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;
    }

	@Override
	public 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 response
                System.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;

	@Override
	protected 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() {

		// 发送请求获取token
		String 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(替换为自己的)

查看微信消息文章来源地址https://www.toymoban.com/news/detail-555795.html

到了这里,关于Java微信公众号发送消息-保姆级教程附源码的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 微信小程序通过公众号服务号发送消息

    一、基础概念: 准备条件:      1、公众号和小程序必须在同一个公司主体下。      2、在公众号后台需要对小程序进程绑定操作。 公众号提供了两种消息,一种是订阅消息,一种是模板消息。 订阅消息需要用户主动订阅,然后才能接收消息,微信提供前端组件用于用户进

    2024年02月07日
    浏览(46)
  • SpringBoot整合调用微信模板方法实现微信公众号消息通知推送,Java实现微信公众号给关注用户推送自定义消息通知(手把手从0到1)

    目录 概述 公众号给关注用户推送自定义消息 一、申请公众号模板消息 二、获取安装“web开发者工具” 三、微信网页授权说明 四、微信网页授权 - 流程时序图 五、HTTPClient 实现微信公众号消息推送与发布(四步走) 六、通过weixin-java-mp SDK实现微信公众号消息推送与发布(七

    2024年02月10日
    浏览(45)
  • 微信小程序生态8-基于weixin-java-mp实现微信公众号被动回复消息

    微信小程序生态1-初识小程序 微信小程序生态2-创建一个微信小程序 微信小程序生态3-微信小程序登录流程设计 微信小程序生态4-扫普通二维码进入小程序、打开短链接进入小程序 微信小程序生态5-微信公众号扫码登录PC端网页 微信小程序生态6-微信公众号授权登录(适用于H

    2024年02月10日
    浏览(52)
  • 【Java项目】SpringBoot项目完成微信公众号收到用户消息自动回复功能附带视频(超详细)

    视频讲解 首先你需要先注册一个你的微信公众号 微信公众号平台 然后打开下面的自动回复功能 之后进入到你的开发者中心 开发者中心基本配置 然后生成你的开发者密码,开发者id,以及设置你的IP白名单。 这里的IP白名单中的IP必须是一个公网IP,因为微信官方会把他们的

    2024年02月13日
    浏览(51)
  • 【Java】企业微信群机器人发送消息(文字、图片、MarkDown、文件消息)

    2022/4/22更新:新增可发送文件消息。 发送文件消息需要先将文件上传到企业微信的临时素材,url为https://qyapi.weixin.qq.com/cgi-bin/webhook/upload_media?type=filekey=**********,这个key就是群机器人Webhook地址的key参数,在代码里我已经处理好了,只要有Webhook地址就行。获取到media_id,再拿

    2023年04月08日
    浏览(46)
  • 工作随记-Java利用企业微信群机器人定时发送消息

    hi,大家好,我是恰恰 阅读本文需要2分钟~ 最近利用企业微信群机器人做的需求主要有 1.返奖率通知与告警: 抽奖箱能抽出垃圾也能抽出大货,每隔5分钟查询一下这个返奖率,如果用户频繁抽出大货,这个抽奖箱的返奖率已经高出了阈值,那么我们就将其下架。 2.自动发送导

    2024年02月09日
    浏览(52)
  • 【全开源】JAVA婚恋相亲红娘牵线系统源码支持微信小程序+微信公众号+H5+APP

    一、我们技术使用JAVA后台服务 前后端分离 springboot+mybatisplus+mysql 用户端 uniapp(vue语法)管理后台 vue+elementUi 适配小程序+H5+公众号 二、演示说明 管理后台演示: 私信客服获取演示地址 用户端 私信客服获取演示地址 三、技术栈 后台服务 springboot+mybatisplus+mysql 用户端 uniapp(

    2024年03月18日
    浏览(71)
  • uniapp+微信小程序获取openId,获取access_token,订阅消息模板,java后台发送消息

    1.前期准备 2.用户订阅消息 3.获取openId(uniapp) 4.获取access_token 5.发送消息 6.请求的代码Springboot(自己写有发送请求方法的可以不用看) 在微信公众号申请订阅消息 在公共模板这里选用模板, 模板种类跟小程序设置的类目有关,只有特殊的类目有长期订阅模板 类目可以在设

    2024年02月03日
    浏览(47)
  • 微信公众号推送天气教程,自动定时推送【Java版】开发者和小白详细教程

    久违的大更新: 建议先打开,因为GitHub在国内加载很慢。 点击打开 Java版本,教程最近一次更新时间为: 2023-08-23 重大更新: 1: 谚语功能修复。 2: 新增农历生日。 已经部署的伙伴,务必在公众号后台更新新的模块以及applicantion.yml配置文件中的模板ID,新的模板在本教程当中

    2024年01月23日
    浏览(77)
  • 微信公众号模板消息源码实现,打破服务号群发推送次数限制

    公众号服务号每个月只能群发推送四次文章,我们可以使用模板消息为公众号粉丝推送信息 下面是使用golang实现的模板消息发送类库封装,轻松实现模板消息发送 wechat.go 我们的使用方式 推送的效果如图所示,点击模板就能跳转到我们自定义的url上 我在自己客服系统中也是

    2024年02月15日
    浏览(51)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包