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

界面:

 测试号界面:

 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(替换为自己的)

查看微信消息

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

最新PHP微信扫码关注公众号并授权登录源码

正文: PHP微信扫码登录看起来简单&#xff0c;但做起来有点麻烦&#xff0c;开发起来就会浪费很多的时间。 PHP判断是否首次关注公众号&#xff0c;扫码关注公众号获取微信用户头像、openid和省市等信息源码。 第一步&#xff1a;获取关注二维码ticket,并且传参二维码qrcode…

大模型“涌现”的思维链,究竟是一种什么能力?

听说最近AI大厂的开发人员和高校的NLP研究人员&#xff0c;都在琢磨&#xff0c;怎么让大模型“涌现”。那画面莫名就让我想到了程序员给服务器上香来保佑不宕机&#xff0c;都有种求诸于天的玄学。 所谓“涌现”,在大模型领域指的是当模型突破某个规模时&#xff0c;性能显著提…

云原生是什么?细数云原生的5大特征

云原生是什么&#xff1f;细数云原生的5大特征 00 云原生是什么&#xff1f;01 轻、快、不变的基础设施02 弹性服务编排03 开发运营一体化04 微服务架构05 无服务模型小结 来源&#xff1a;大数据DT 导读&#xff1a;随着公有云和私有云的广泛部署&#xff0c;云计算基础设施成…

1分钟快速了解芝数云“大数据”实训场景!

点击蓝字&#xff0c;关注我们 大数据集群搭建耗时长&#xff1f;电脑挂机后中断&#xff0c;无法恢复进度怎么办&#xff1f;大数据集群搭建循环反复&#xff0c;永远停留在第一步&#xff1f;大数据实训过程中&#xff0c;一步错步步错&#xff0c;只能从头开始... ... 芝诺数…

产品更新 | 芝数云全新升级 打造线上教学新体验!

点击蓝字 关注我们 - The World AIDS Day - - 芝 数 云 升 级 啦 - 为了教师更加方便的开展教学工作&#xff0c;学生更加便捷的进行上课&#xff0c;数字素养与技能提升平台于12月12日晚全面升级&#xff0c;从页面以及性能方面进行了优化&#xff0c;整体焕然一新。 焕…

推动数云融合,神州数码要做企业领先的数字化转型合作伙伴

众所周知&#xff0c;伴随着数字经济的蓬勃发展&#xff0c;各种数字技术创新正在给传统行业带来颠覆式的变革&#xff0c;数字化转型也成为了广大企业未来发展的必经之路。 那么问题就来了&#xff1a;面对新时代的数字化大潮&#xff0c;企业应该选择一条怎样的数字化转型之路…

数云运维总监陈延宗:基于阿里云计算巢,数云CRM一键云上交付

12月21日&#xff0c;在弹性计算年度峰会上&#xff0c;数云CRM运维总监陈延宗发表了主题为《计算巢最佳实践--数云CRM一键云上交付》的演讲&#xff0c;介绍了数云CRM在阿里云计算巢平台的最佳实践。 图&#xff1a;数云CRM运维总监陈延宗 01 传统交付的四个痛点 数云目前已发…

校历第九周 cf+数云图杯 做题笔记

CF1754 A. Technical Support &#xff08;思维、模拟、贪心&#xff09; https://codeforces.com/contest/1754/problem/A /** Author: xiao-p* Date: 2022-10-27 20:31:41* LastEditors: wzp 304535326qq.com* LastEditTime: 2022-10-27 20:46:10* Description: 304535326q…

百数云最新功能webAPI推送数据样例

百数云最新增加了webAPI数据推送的功能。 必须有数据推送的样例才能进行进一步处理,下面是我得到的推送的数据样例。 # 测试数据触发 {} # 新增数据触发 {eventType: data_create.web.one,eventData: {fieldsData

新生代采集器“易数云”--入门版教学

**背景&#xff1a;**现如今很多白领或学生一族&#xff0c;迫于工作或者学习的压力&#xff0c;经常会需要一些数据作为工作或者学习上的用途。碍于市面上常见的采集器“编写爬虫规则难又或者数据费用高”的压力&#xff0c;很多人望而兴叹。今天我们就为大家带来一款比较容易…

驭数云低代码平台简介

1.1 驭数云低代码平台简介 1.概述 编码枪代码生成器是目前国内第一家从运维到开发都在线上完成的代码生成器。通过简单的在线设计&#xff0c;便可完成70%左右的网站或APP项目的开发任务&#xff0c;编码枪使用说明书将会带你了解代码生成器的使用&#xff0c;使本产品更加易…

数云融合打造数字化新引擎!

本期作者&#xff1a;李刚 神州数码集团 副总裁 CTO 几年前&#xff0c;一颗新星在汽车制造领域可谓是“横空出世”&#xff0c;一举成为全球市值最大的车企&#xff0c;开启了世界新能源汽车的新潮流。这&#xff0c;就是特斯拉。 短短几年内取得如此卓越成就&#xff0c;特斯…

聊天机器人简要理解

聊天机器人 身边的同事或多或少都在聊ChatGPT&#xff0c;这又使得学生时代训练模型的恐惧感开始支配我。说起聊天机器人&#xff0c;我们先熟悉一下不同对话系统的概念。 一、对话系统分类 1、单轮对话 单轮与传统的问答系统相类似&#xff0c;是智能对话系统的初级应用。…

如何让ChatGPT学习销售过往文字稿,并自动分析话术的优缺点,然后修改?

该场景对应的关键词库&#xff08;13个&#xff09;&#xff1a; 产品特点、服务优势、目标客户需求和痛点、行业与市场的趋势变化、竞争对手的优劣势、销售场景的特点和要求、销售环境、销售氛围、时间、地点、客户情感需求、心理转态、个性特点、解决方案 提问模板&#xf…

chatgpt赋能python:PythonIP匹配

Python IP匹配 随着互联网的不断发展&#xff0c;IP地址已成为人们最常使用的一种网络标识。在网络分析和开发中&#xff0c;经常会用到IP地址的相关操作&#xff0c;如IP地址的匹配。Python作为一种性能比较好的语言&#xff0c;也可以很好地完成IP地址的匹配工作。本文将介绍…

chatgpt赋能python:Python查看局域网内所有IP

Python查看局域网内所有IP Python是一种流行的编程语言&#xff0c;可用于网络编程和系统管理。Python程序可以访问本地网络并查找设备。如果您需要查找局域网中的所有IP地址&#xff0c;Python是一个非常方便的工具。在本文中&#xff0c;我们将介绍如何使用Python编写程序以…

chatgpt赋能Python-python_ip摄像头

Python IP摄像头介绍 IP摄像头技术的快速发展&#xff0c;使得越来越多的人开始使用IP摄像头来保护家庭、企业和公共场所的安全。Python语言的简洁、优雅和易于学习&#xff0c;使得它成为越来越多人选择的编程语言。在本文中&#xff0c;我们将介绍如何使用Python编程来控制I…

chatgpt赋能python:Python输入IP地址

Python 输入IP地址 在网络技术中&#xff0c;IP地址是每一个网络设备在互联网中的唯一身份标识。IP地址代表Internet协议&#xff08;IP&#xff09;网络中的一个设备&#xff0c;可以是计算机或其他设备。在Python中输入IP地址时&#xff0c;通常使用.来分隔数字&#xff0c;…

chatgpt赋能Python-python_ping_ip

Python Ping IP - 介绍 Python是一种高级编程语言&#xff0c;因其简单易学、代码可读性高和可扩展性强而备受青睐。Python可以用于各种不同的应用程序和项目&#xff0c;其中之一就是网络管理和监控。在这篇结合SEO的文章中&#xff0c;我们将探讨如何使用Python ping IP地址…

chatgpt赋能Python-python_ip地址转换

Python IP地址转换&#xff1a;原理、常用函数和示例 在网络编程中&#xff0c;IP地址是一个很重要的概念。Python提供了很多IP地址转换函数&#xff0c;这篇文章将讲解其原理、常用函数和示例。 IP地址转换原理 IP地址是由32位表示的二进制数&#xff0c;它是计算机在网络上…