微信公眾號(hào)開發(fā)消息推送功能
微信公眾號(hào)開發(fā)
運(yùn)行效果

微信公眾號(hào)簡(jiǎn)介
微信公眾號(hào)分為服務(wù)號(hào)、訂閱號(hào)、企業(yè)號(hào),訂閱號(hào)可以個(gè)人申請(qǐng),服務(wù)號(hào)和企業(yè)號(hào)要有企業(yè)資質(zhì)才可以。
我們所說的微信公眾號(hào)開發(fā)指的是訂閱號(hào)和服務(wù)號(hào)。關(guān)于訂閱號(hào)和服務(wù)器的區(qū)別,官方是這樣解釋的
- 服務(wù)號(hào):主要偏向于服務(wù)交互(功能類似12315,114,銀行,提供綁定信息,服務(wù)交互),每月可群發(fā)4條消息;服務(wù)號(hào)**適用人群:媒體、企業(yè)、政府或其他組織。
- 訂閱號(hào):主要偏向于為用戶傳達(dá)資訊,(功能類似報(bào)紙雜志,為用戶提供新聞信息或娛樂趣事),每天可群發(fā)1條消息;訂閱號(hào)**適用人群:個(gè)人、媒體、企業(yè)、政府或其他組織。
注冊(cè)微信公眾號(hào)
進(jìn)入微信公眾號(hào)注冊(cè)頁面https://mp.weixin.qq.com/點(diǎn)擊公眾號(hào)右上方的注冊(cè)按鈕,進(jìn)入注冊(cè)界面,填寫基本信息,選擇訂閱號(hào), 完成身份認(rèn)證, 即可。
注冊(cè)測(cè)試公眾號(hào)
個(gè)人訂閱號(hào)有一些接口是沒有權(quán)限的,也就是說個(gè)人訂閱號(hào)無法調(diào)用一些高級(jí)的權(quán)限接口,如生成二維碼、網(wǎng)頁授權(quán)、自定義菜單、微信支付這樣的接口權(quán)限個(gè)人訂閱號(hào)是沒有調(diào)用權(quán)限的, 幸運(yùn)的是,微信公眾平臺(tái)提供了測(cè)試公眾賬號(hào),測(cè)試公眾號(hào)有很多個(gè)人訂閱號(hào)不具備的權(quán)限, 測(cè)試公眾號(hào)的注冊(cè)地址為:
http://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login
用微信掃描頁面中的二維碼進(jìn)行登錄,登錄成功后,就可以看到騰訊分配給我們的測(cè)試公眾號(hào)的信息了,如下圖所示, 接下來我們就可以搭建環(huán)境,進(jìn)行開發(fā)測(cè)試了

測(cè)試公眾號(hào)的所擁有的接口權(quán)限如下:


搭建微信本地調(diào)試環(huán)境
開發(fā)基于微信公眾號(hào)的應(yīng)用最大的痛苦之處就是調(diào)試問題,每次實(shí)現(xiàn)一個(gè)功能后都需要部署到一個(gè)公網(wǎng)服務(wù)器進(jìn)行測(cè)試,因?yàn)槲⑿庞脩裘看蜗蚬娞?hào)發(fā)起請(qǐng)求時(shí),微信服務(wù)器會(huì)先接收到用戶的請(qǐng)求,然后再轉(zhuǎn)發(fā)到我們的服務(wù)器上,也就是說,微信服務(wù)器是要和我們的服務(wù)器進(jìn)行網(wǎng)絡(luò)交互,所以我們必須保證我們的服務(wù)器外網(wǎng)可以訪問到,這種部署到公網(wǎng)服務(wù)器進(jìn)行測(cè)試的做法對(duì)于我們開發(fā)者來說簡(jiǎn)直是噩夢(mèng)。所以我們要想一個(gè)辦法可以做到本地部署,本地調(diào)試代碼,而要做到這一點(diǎn),那么我們要解決的問題就是將內(nèi)網(wǎng)的部署服務(wù)器映射到外網(wǎng),讓微信服務(wù)器可以正常訪問到,幸運(yùn)的是,借助于第三方軟件Ngrok,我們就可以做得到。Ngrok是一個(gè)免費(fèi)的軟件Ngrok,使用Ngrok后,我們就可以實(shí)現(xiàn)內(nèi)網(wǎng)穿透,也就是說我們可以將內(nèi)網(wǎng)的服務(wù)器映射到外網(wǎng)給別人訪問,這對(duì)于我們?cè)诒镜亻_發(fā)環(huán)境中調(diào)試微信代碼是以及給用戶演示一些東西非??焖俸陀袔椭?因?yàn)榭梢灾苯邮褂梦覀冏约旱膬?nèi)網(wǎng)的電腦作為服務(wù)器。不過需要翻墻訪問.常用的內(nèi)網(wǎng)穿透工具有natapp,ngrok,dingding,關(guān)于微信公眾號(hào)開發(fā),這三個(gè)工具我都使用了,只有natapp可以正常開發(fā)。
關(guān)于natapp的使用網(wǎng)上很多,我在這里就不在介紹了。
natapp成功標(biāo)志:

可以通過訪問http://xt77eg.natappfree.cc訪問到我們本機(jī)的服務(wù)
微信公眾號(hào)接入(校驗(yàn)簽名)
開發(fā)者提交信息后,微信服務(wù)器將發(fā)送GET請(qǐng)求到填寫的服務(wù)器地址URL上,GET請(qǐng)求攜帶參數(shù)如下表所示:

開發(fā)者通過檢驗(yàn)signature對(duì)請(qǐng)求進(jìn)行校驗(yàn)(下面有校驗(yàn)方式)。若確認(rèn)此次GET請(qǐng)求來自微信服務(wù)器,請(qǐng)?jiān)瓨臃祷豦chostr參數(shù)內(nèi)容,則接入生效,成為開發(fā)者成功,否則接入失敗。加密/校驗(yàn)流程如下:
1)將token、timestamp、nonce三個(gè)參數(shù)進(jìn)行字典序排序
2)將三個(gè)參數(shù)字符串拼接成一個(gè)字符串進(jìn)行sha1加密
3)開發(fā)者獲得加密后的字符串可與signature對(duì)比,標(biāo)識(shí)該請(qǐng)求來源于微信
@Controller
@RequestMapping(value = "wx")
public class WeiController{
/**
* 公眾號(hào)appid
*/
@Value("${wx.appid}")
private String appid;
/**
* 公眾號(hào)appSecret
*/
@Value("${wx.secret}")
private String secret;
/**
* 微信消息接收和token驗(yàn)證
* @param request
* @param response
* @throws IOException
*/
@GetMapping("/weChatToken")
public void weChat(HttpServletRequest request, HttpServletResponse response) {
boolean isGet = request.getMethod().toLowerCase().equals("get");
if (isGet) {
// 微信加密簽名
String signature = request.getParameter("signature");
// 時(shí)間戳
String timestamp = request.getParameter("timestamp");
// 隨機(jī)數(shù)
String nonce = request.getParameter("nonce");
// 隨機(jī)字符串
String echostr = request.getParameter("echostr");
// 通過檢驗(yàn)signature對(duì)請(qǐng)求進(jìn)行校驗(yàn),若校驗(yàn)成功則原樣返回echostr,表示接入成功,否則接入失敗
if (signature != null && CheckoutUtil.checkSignature(signature, timestamp, nonce)) {
try {
PrintWriter print = response.getWriter();
print.write(echostr);
print.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
public class CheckoutUtil {
public static String token = "999";
/**
* 驗(yàn)證簽名
* @param signature
* @param timestamp
* @param nonce
* @return
*/
public static boolean checkSignature(String signature, String timestamp, String nonce) {
String[] arr = new String[] { token, timestamp, nonce };
// 將token、timestamp、nonce三個(gè)參數(shù)進(jìn)行字典序排序
Arrays.sort(arr);
StringBuilder content = new StringBuilder();
for (int i = 0; i < arr.length; i++) {
content.append(arr[i]);
}
MessageDigest md = null;
String tmpStr = null;
try {
md = MessageDigest.getInstance("SHA-1");
// 將三個(gè)參數(shù)字符串拼接成一個(gè)字符串進(jìn)行sha1加密
byte[] digest = md.digest(content.toString().getBytes());
tmpStr = byteToHex(digest );
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
// 將sha1加密后的字符串可與signature對(duì)比,標(biāo)識(shí)該請(qǐng)求來源于微信
return tmpStr != null ? tmpStr.equals(signature) : false;
}
/**
* 十六進(jìn)制字節(jié)數(shù)組轉(zhuǎn)為字符串
* @param hash
* @return
*/
private static String byteToHex(final byte[] hash) {
Formatter formatter = new Formatter();
for (byte b : hash) {
formatter.format("%02x", b);
}
String result = formatter.toString();
formatter.close();
return result;
}
進(jìn)入微信測(cè)試公眾號(hào)管理界面,在接口配置信息中填入映射的外網(wǎng)地址和代碼中聲明的token,如下圖所示:
點(diǎn)擊提交,會(huì)顯示配置成功,如下圖:
![[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-bPMvNxRI-1608702695943)(typora-user-images\image-20201214105001081.png)]](http://img.jbzj.com/file_images/article/202302/20230215151454121.png)
到此,我們的公眾號(hào)應(yīng)用已經(jīng)能夠和微信服務(wù)器正常通信了,也就是說我們的公眾號(hào)已經(jīng)接入到微信公眾平臺(tái)了。
給指定用戶推送消息
網(wǎng)頁授權(quán)獲取用戶openid
如果用戶在微信客戶端中訪問第三方網(wǎng)頁,公眾號(hào)可以通過微信網(wǎng)頁授權(quán)機(jī)制,來獲取用戶基本信息(openId),進(jìn)而實(shí)現(xiàn)業(yè)務(wù)邏輯。
關(guān)于網(wǎng)頁授權(quán)回調(diào)域名的說明:
1、在微信公眾號(hào)請(qǐng)求用戶網(wǎng)頁授權(quán)之前,開發(fā)者需要先到公眾平臺(tái)官網(wǎng)中的“開發(fā) - 接口權(quán)限 - 網(wǎng)頁服務(wù) - 網(wǎng)頁帳號(hào) - 網(wǎng)頁授權(quán)獲取用戶基本信息”的配置選項(xiàng)中,修改授權(quán)回調(diào)域名。請(qǐng)注意,這里填寫的是域名(是一個(gè)字符串),而不是URL,因此請(qǐng)勿加 http:// 等協(xié)議頭;
2、授權(quán)回調(diào)域名配置規(guī)范為全域名,比如需要網(wǎng)頁授權(quán)的域名為:www.qq.com,配置以后此域名下面的頁面http://www.qq.com/music.html 、 http://www.qq.com/login.html 都可以進(jìn)行OAuth2.0鑒權(quán)。但http://pay.qq.com 、 http://music.qq.com 、 http://qq.com 無法進(jìn)行OAuth2.0鑒權(quán)
3、如果公眾號(hào)登錄授權(quán)給了第三方開發(fā)者來進(jìn)行管理,則不必做任何設(shè)置,由第三方代替公眾號(hào)實(shí)現(xiàn)網(wǎng)頁授權(quán)即可
獲取用戶openId步驟:
1、引導(dǎo)用戶進(jìn)入授權(quán)頁面同意授權(quán),獲取code
2、通過code換取openId
代碼如下:
@Controller
@RequestMapping(value = "wx")
public class WeiController{
private String appid="微信公眾號(hào)的appid";
private String secret="微信公眾號(hào)的secret";
/**
* 獲取微信用戶code,并重定向獲取用戶openId
* @return
*/
@GetMapping("/getUserCode")
public String getUserCode(){
String backUrl = "http://xt77eg.natappfree.cc/wx/getUserOpenId";
String getOpenIdUrl = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri="+ backUrl+"&response_type=code&scope=snsapi_base&state=STATE#wechat_redirect";
getOpenIdUrl = getOpenIdUrl.replace("APPID",appid);
return "redirect:" + getOpenIdUrl;
}
/**
* 獲取用戶openId
* @return
* @throws IOException
*/
@GetMapping("/getUserOpenId")
@ResponseBody
public String getUserOpenId()throws IOException{
//獲取code
String code = request.getParameter("code");
//換取用戶openid
String url="https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code";
url=url.replace("APPID", appid).replace("SECRET", secret).replace("CODE", code);
JSONObject result = Util.doGetJson(url);
JSONObject jSONObject = JSONObject.parseObject(String.valueOf(result));
String openid = jSONObject.getString("openid");
return openid;
}
給指定用戶發(fā)送模板信息
首先要準(zhǔn)備一個(gè)模板,測(cè)試號(hào)可自定義模板,但在正式公眾號(hào)我們要申請(qǐng),或者使用別人已經(jīng)申請(qǐng)過的模板。
![[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-KtE1NG6h-1608702695950)(typora-user-images\image-20201214111214457.png)]](http://img.jbzj.com/file_images/article/202302/20230215151454122.png)
pom:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--微信模版消息推送三方sdk-->
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-mp</artifactId>
<version>3.3.0</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.8</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.58</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test</artifactId>
<version>2.1.8.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
Controller:
@Controller
@RequestMapping(value = "wx")
public class WeiController{
@Resource
PushMessageService pushMessageService;
/**
* 向每個(gè)用戶推送消息
* @return
*/
@GetMapping("/sendMessage")
@ResponseBody
public String sendMessage(){
String openId = "用戶openId";
if(!"".equals(openId)){
AlarmParamsDTO dto = new AlarmParamsDTO("申請(qǐng)進(jìn)度", "國(guó)家獎(jiǎng)學(xué)金", "申請(qǐng)通過", time, "成功");
dto.setOpenId(openId);
pushMessageService.pushMessage(dto);
}
return "success";
}Service:
@Service
@Slf4j
public class PushMessageServiceImpl implements PushMessageService{
private String appid="微信公眾號(hào)appid";
private String secret="微信公眾號(hào)secret";
/**
* 給微信公眾號(hào)某個(gè)用戶推送信息
* @param alarmParamsDTO
*/
@Override
public void pushMessage(AlarmParamsDTO alarmParamsDTO) {
//1,配置
WxMpInMemoryConfigStorage wxStorage = new WxMpInMemoryConfigStorage();
wxStorage.setAppId(appid);
wxStorage.setSecret(secret);
WxMpService wxMpService = new WxMpServiceImpl();
wxMpService.setWxMpConfigStorage(wxStorage);
List<WxMpTemplateData> wxMpTemplateData = Arrays.asList(
new WxMpTemplateData("first",alarmParamsDTO.getFirst(),"#000000"),
new WxMpTemplateData("keyword1",alarmParamsDTO.getKeyword1(),"#000080"),
new WxMpTemplateData("keyword2",alarmParamsDTO.getKeyword2(),"#0000FF"),
new WxMpTemplateData("keyword3",alarmParamsDTO.getKeyword3(),"#FFD700"),
new WxMpTemplateData("remark",alarmParamsDTO.getRemark(),"#00FF00")
);
//2,推送消息
WxMpTemplateMessage templateMessage = WxMpTemplateMessage.builder()
.toUser(alarmParamsDTO.getOpenId())
.templateId("tIDrdFcqFGMsTnc462H49_DbjgXUuIjsqIlQttq7VDE")
.data(wxMpTemplateData)
.url("http://www.baidu.com")
.build();
try {
wxMpService.getTemplateMsgService().sendTemplateMsg(templateMessage);
} catch (Exception e) {
System.out.println("推送失?。? + e.getMessage());
}
}
}
entity:
@Setter
@Getter
@NoArgsConstructor
@AllArgsConstructor
public class AlarmParamsDTO {
/**
* 推送信息小標(biāo)題
*/
private String first;
/**
* 學(xué)生姓名
*/
private String keyword1;
/**
* 申請(qǐng)資助類型
*/
private String keyword2;
/**
* 申請(qǐng)狀態(tài)
*/
private String keyword3;
/**
* 申請(qǐng)結(jié)果
*/
private String remark;
/**
* 用戶微信openId,唯一標(biāo)識(shí)
*/
private String openId;
public AlarmParamsDTO(String first, String keyword1, String keyword2, String keyword3, String remark) {
this.first = first;
this.keyword1 = keyword1;
this.keyword2 = keyword2;
this.keyword3 = keyword3;
this.remark = remark;
}
}
到此這篇關(guān)于微信公眾號(hào)開發(fā)消息推送功能的文章就介紹到這了,更多相關(guān)微信公眾號(hào)開發(fā)消息推送內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
IDEA配置maven環(huán)境的詳細(xì)教程(Unable to import maven project報(bào)錯(cuò)問題的解決)
這篇文章主要介紹了IDEA配置maven環(huán)境的詳細(xì)教程(Unable to import maven project問題的解決),本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-06-06
Java基于Semaphore構(gòu)建阻塞對(duì)象池
這篇文章主要介紹了Java基于Semaphore構(gòu)建阻塞對(duì)象池,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-04-04
try-cache-finally讀取文件錯(cuò)誤try-with-resources使用方法
這篇文章主要為大家介紹了try-cache-finally讀取文件錯(cuò)誤try-with-resources使用方法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-02-02
一文搞懂Java設(shè)計(jì)模式之責(zé)任鏈模式
這篇文章主要給大家介紹了關(guān)于Java設(shè)計(jì)模式之責(zé)任鏈模式的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12
SpringBoot使用自動(dòng)配置xxxAutoConfiguration
這篇文章介紹了SpringBoot自動(dòng)配置xxxAutoConfiguration的使用方法,對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-12-12

