背景:
系统监控功能,当监控项达到阈值,企业微信发送消息给指定的人
1.选型
企业微信消息推送分两种:
1.通过应用推送给指定的人
2.往群里推送消息(需要把相关人员加入群里)
选择第一种通过应用推送,感觉更灵活
2.实现
获取三个重要参数(企业id,应用号码,密钥)
调用官方两个核心接口
1.通过企业id和密钥获取token(官网地址:developer.work.weixin.qq.com/document/path/91039)
2.通过token发送消息(官网地址:developer.work.weixin.qq.com/document/path/90236)
代码:
package cn.vpclub.moses.servermonitor.weChat;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
@Slf4j
@Component
public class WeChatSender {
@Value("${wechat.tokenUrl}")
private String tokenUrl;//获取token的url
@Value("${wechat.sendUrl}")
private String sendUrl;//发送消息url
@Value("${wechat.agentId}")
private String agentId;//应用id
@Value("${wechat.cropId}")
private String cropId;//公司id
@Value("${wechat.cropSecret}")
private String cropSecret;//密钥
private static Gson gson = new Gson();
public void sendMessage(String contentValue, String touser) {
try {
// 发送消息
log.info("wechat send message begin message:{},touser:{}", contentValue, touser);
String response = post(createPostData(touser, contentValue), this.getToken());
log.info("wechat send message end return:{}", response);
} catch (IOException e) {
log.error("wechat push message error,message:{}", contentValue);
}
}
private String getToken() throws IOException {
CloseableHttpClient httpClient = HttpClients.createDefault();
HttpGet httpGet = new HttpGet(tokenUrl + "?corpid=" + cropId + "&corpsecret=" + cropSecret);
CloseableHttpResponse response = httpClient.execute(httpGet);
log.info("wechatSender getToken return", response.toString());
String resp;
try {
HttpEntity entity = response.getEntity();
System.out.println(response.getAllHeaders());
resp = EntityUtils.toString(entity, "utf-8");
EntityUtils.consume(entity);
} finally {
response.close();
}
Map<String, Object> map = gson.fromJson(resp,
new TypeToken<Map<String, Object>>() {
}.getType());
return map.get("access_token").toString();
}
private String createPostData(String touser, String contentValue) {
Map<String, Object> weChatData = new HashMap<>();
weChatData.put("touser", touser);
weChatData.put("agentid", agentId);
weChatData.put("msgtype", "text");
Map<Object, Object> content = new HashMap<>();
content.put("content", contentValue);
weChatData.put("text", content);
log.info("wechat createPostData:{}", gson.toJson(weChatData));
return gson.toJson(weChatData);
}
private String post(String data, String token) throws IOException {
CloseableHttpClient httpclient = HttpClients.createDefault();
HttpPost httpPost = new HttpPost(sendUrl + token);
httpPost.setHeader("Content-Type", "Content-Type");
httpPost.setEntity(new StringEntity(data, "utf-8"));
CloseableHttpResponse response = httpclient.execute(httpPost);
log.info("wechat send return:{}", response);
String resp;
try {
HttpEntity entity = response.getEntity();
resp = EntityUtils.toString(entity, "utf-8");
EntityUtils.consume(entity);
} finally {
response.close();
}
return resp;
}
}
3.设置接收消息服务器URL
做完第二步后我以为结束了,结果消息发不出去,报错我的本机ip没有发消息的权限
解决:要在企业微信加可信ip
企业微信1.0版本可以直接加(不用管)
企业微信2.0版本要求先加【可信域名】或【接收消息服务器URL】才能加可信ip
【可信域名】或【接收消息服务器URL】本质上都是企业微信会去请求对应的【域名】或【url】,并且得到预期的值才会推送消息。就是附加了一层保障,确认消息推送的请求是我们发出去的。
因为【可信域名】需要使用80端口,所以采用了【接收消息服务器URL】的方式,感觉更灵活
【接收消息服务器URL】大体流程:调用企业微信推送消息后,企业微信会回调我们设置的接口,我们解密后对比签名是否一致,如果一致再解密消息参数,然后把解密出来的消息返回,企业微信确认消息是最初的消息则进行消息推送。
加密解密一大堆,官网:developer.work.weixin.qq.com/document/path/90968?vid=1688857843457002&deviceid=76843fa6-db53-4ed9-a7fe-4462f5952416&version=4.1.3.6008&platform=win#%E5%8A%A0%E5%AF%86%E5%87%BD%E6%95%B0
但是:官方有代码示例,上面说的都不用你管。把官方提供的加密解密代码搞下来丢进自己的项目,然后直接调用就好了,地址:developer.work.weixin.qq.com/document/path/90307)
官方加密解密下载地点
解压后密解密代码路径
调用示例
代码
@RestController
@RequestMapping("/wechat")
public class WeChatController {
@Autowired
Callback callback;
/**
* @param msg_signature 企业微信加密签名,msg_signature结合了企业填写的token、请求中的timestamp、nonce参数、加密的消息体
* @param timestamp 时间戳
* @param nonce 随机数
* @param echostr 加密的字符串。需要解密得到消息内容明文,解密后有random、msg_len、msg、CorpID四个字段,其中msg即为消息内容明文
* @return
* @throws AesException
*/
@RequestMapping("/messageConfirm")
public String messageConfirm(@RequestParam String msg_signature, @RequestParam String timestamp, @RequestParam String nonce, @RequestParam String echostr) {
return callback.messageConfirm(msg_signature, timestamp, nonce, echostr);
}
}
@Slf4j
@Component
public class Callback {
@Value("${wechat.callbackToken}")
private String callbackToken;//回调token。可以自己定义也可以去企业微信随机生成
@Value("${wechat.callbackEncodingAESKey}")
private String callbackEncodingAESKey;//回调密钥。可以自己定义也可以去企业微信随机生成
@Value("${wechat.cropId}")
private String cropId;//公司id
public String messageConfirm(String msgSignature, String timestamp, String nonce, String echostr) {
try {
WXBizMsgCrypt wxcpt = new WXBizMsgCrypt(callbackToken, callbackEncodingAESKey, cropId);
return wxcpt.VerifyURL(msgSignature, timestamp, nonce, echostr);
} catch (AesException e) {
log.info("wechat messageConfirm error,code:{},message:{}", e.getCode(), e.getMessage());
}
return null;
}
}
回调模式验证
接口写好后,先发到测试环境(【接收消息服务器URL】需要公网地址(你的本机ip是不可以的,那是局域网),如果是内网系统没有公网地址就要去做内网穿透了--一般安全性要求这么高的系统也不会用企业微信去推系统消息),然后用【测试回调模式】去验证
地址:open.work.weixin.qq.com/wwopen/devtool/interface/combine?vid=1688857843457002&cst=04EAB36DA5210A5B2E3A0F85189683906D8406D9DBD12EFF64BE38560124CC421CBF753B7980E7BBF5A9D7971B401E3E4F77E7EAED15D0D2C854EB915877E7CD&deviceid=76843fa6-db53-4ed9-a7fe-4462f5952416&version=4.1.3.6008&platform=win
测试成功后,去配置【接收消息服务器URL】
保存成功后记得把你的本机ip加入可信ip(在报错-ip没有发送消息权限时,报错信息里会有个ip,把这个ip加入可信ip)
发给谁
touser 可以写@all表示发给所有人,发送给指定的人可以用userid1|userId2的方式发送,但是userId怎么来的,在企业微信的应用里找了半天也没找到,联系本司企业微信管理员也不知道是什么。一番寻找发现蛛丝马迹,官网的服务端API有个手机号获取userid(地址:developer.work.weixin.qq.【delete】com/document/path/95402)。通过多个手机号验证发现userId是姓.名的方式,如张三的userId就是zhang.san