<properties>
<weixin-java-mp.version>3.0.0weixin-java-mp.version>
properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.github.binarywanggroupId>
<artifactId>weixin-java-mpartifactId>
<version>${weixin-java-mp.version}version>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>fastjsonartifactId>
<version>${fastjson.version}version>
dependency>
dependencies>
dependencyManagement>
<dependency>
<groupId>com.github.binarywanggroupId>
<artifactId>weixin-java-mpartifactId>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>fastjsonartifactId>
dependency>
<dependency>
<groupId>com.github.binarywanggroupId>
<artifactId>weixin-java-mpartifactId>
<version>3.0.0version>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>fastjsonartifactId>
<version>1.2.47version>
dependency>
目录结构:
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import javax.net.ssl.*;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Map;
@Slf4j
public class HttpUtil {
protected static final String POST_METHOD = "POST";
private static final String GET_METHOD = "GET";
static {
TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
log.debug("ClientTrusted");
}
@Override
public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
log.debug("ServerTrusted");
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[]{};
}
}};
HostnameVerifier doNotVerify = (s, sslSession) -> true;
try {
SSLContext sc = SSLContext.getInstance("SSL", "SunJSSE");
sc.init(null, trustAllCerts, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
HttpsURLConnection.setDefaultHostnameVerifier(doNotVerify);
} catch (Exception e) {
log.error("Initialization https impl occur exception : {}", e);
}
}
/**
* 默认的http请求执行方法
*
* @param url url 路径
* @param method 请求的方法 POST/GET
* @param map 请求参数集合
* @param data 输入的数据 允许为空
* @return result
*/
private static String HttpDefaultExecute(String url, String method, Map<String, String> map, String data) {
String result = "";
try {
url = setParmas(url, map, null);
result = defaultConnection(url, method, data);
} catch (Exception e) {
log.error("出错参数 {}", map);
}
return result;
}
public static String httpGet(String url, Map<String, String> map) {
return HttpDefaultExecute(url, GET_METHOD, map, null);
}
public static String httpPost(String url, Map<String, String> map, String data) {
return HttpDefaultExecute(url, POST_METHOD, map, data);
}
/**
* 默认的https执行方法,返回
*
* @param url url 路径
* @param method 请求的方法 POST/GET
* @param map 请求参数集合
* @param data 输入的数据 允许为空
* @return result
*/
private static String HttpsDefaultExecute(String url, String method, Map<String, String> map, String data) {
try {
url = setParmas(url, map, null);
log.info(data);
return defaultConnection(url, method, data);
} catch (Exception e) {
log.error("出错参数 {}", map);
}
return "";
}
public static String doGet(String url, Map<String, String> map) {
return HttpsDefaultExecute(url, GET_METHOD, map, null);
}
public static String doPost(String url, Map<String, String> map, String data) {
return HttpsDefaultExecute(url, POST_METHOD, map, data);
}
/**
* @param path 请求路径
* @param method 方法
* @param data 输入的数据 允许为空
* @return
* @throws Exception
*/
private static String defaultConnection(String path, String method, String data) throws Exception {
if (StringUtils.isBlank(path)) {
throw new IOException("url can not be null");
}
String result = null;
URL url = new URL(path);
HttpURLConnection conn = getConnection(url, method);
if (StringUtils.isNotEmpty(data)) {
OutputStream output = conn.getOutputStream();
output.write(data.getBytes(StandardCharsets.UTF_8));
output.flush();
output.close();
}
if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
InputStream input = conn.getInputStream();
result = IOUtils.toString(input, StandardCharsets.UTF_8);
input.close();
conn.disconnect();
}
return result;
}
/**
* 根据url的协议选择对应的请求方式
* @param url 请求路径
* @param method 方法
* @return conn
* @throws IOException 异常
*/
//待改进
protected static HttpURLConnection getConnection(URL url, String method) throws IOException {
HttpURLConnection conn;
if (StringUtils.equals("https", url.getProtocol())) {
conn = (HttpsURLConnection) url.openConnection();
} else {
conn = (HttpURLConnection) url.openConnection();
}
if (conn == null) {
throw new IOException("connection can not be null");
}
conn.setRequestProperty("Pragma", "no-cache");// 设置不适用缓存
conn.setRequestProperty("Cache-Control", "no-cache");
conn.setRequestProperty("Connection", "Close");// 不支持Keep-Alive
conn.setUseCaches(false);
conn.setDoOutput(true);
conn.setDoInput(true);
conn.setInstanceFollowRedirects(true);
conn.setRequestMethod(method);
conn.setConnectTimeout(8000);
conn.setReadTimeout(8000);
return conn;
}
/**
* 根据url
* @param url 请求路径
* @return isFile
* @throws IOException 异常
*/
//待改进
protected static HttpURLConnection getConnection(URL url, boolean isFile) throws IOException {
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
if (conn == null) {
throw new IOException("connection can not be null");
}
//设置从httpUrlConnection读入
conn.setDoInput(true);
conn.setDoOutput(true);
conn.setUseCaches(false);
//如果是上传文件,则设为POST
if (isFile) {
conn.setRequestMethod(POST_METHOD); //GET和 POST都可以 文件略大改成POST
}
// 设置请求头信息
conn.setRequestProperty("Connection", "Keep-Alive");
conn.setRequestProperty("Charset", String.valueOf(StandardCharsets.UTF_8));
conn.setConnectTimeout(8000);
conn.setReadTimeout(8000);
return conn;
}
/**
* 拼接参数
* @param url 需要拼接参数的url
* @param map 参数
* @param charset 编码格式
* @return 拼接完成后的url
*/
public static String setParmas(String url, Map<String, String> map, String charset) throws Exception {
String result = StringUtils.EMPTY;
boolean hasParams = false;
if (StringUtils.isNotEmpty(url) && map != null && !map.isEmpty()) {
StringBuilder builder = new StringBuilder();
for (Map.Entry<String, String> entry : map.entrySet()) {
String key = entry.getKey().trim();
String value = entry.getValue().trim();
if (hasParams) {
builder.append("&");
} else {
hasParams = true;
}
if (StringUtils.isNotEmpty(charset)) {
builder.append(key).append("=").append(URLEncoder.encode(value, charset));
} else {
builder.append(key).append("=").append(value);
}
}
result = builder.toString();
}
URL u = new URL(url);
if (StringUtils.isEmpty(u.getQuery())) {
if (url.endsWith("?")) {
url += result;
} else {
url = url + "?" + result;
}
} else {
if (url.endsWith("&")) {
url += result;
} else {
url = url + "&" + result;
}
}
log.debug("request url is {}", url);
return url;
}
}
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate(ClientHttpRequestFactory factory) {
return new RestTemplate(factory);
}
@Bean
public ClientHttpRequestFactory simpleClientHttpRequestFactory() {
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
factory.setConnectTimeout(15*1000);
factory.setReadTimeout(60*60*1000);
return factory;
}
}
import lombok.Data;
import java.util.TreeMap;
@Data
public class WeChatTemplate {
private String touser; //接收者openid
private String template_id; //模板ID
private String url; //URL置空,则在发送后,点击模板消息会进入一个空白页面(ios),或无法点击(android)
private TreeMap<String, TreeMap<String, String>> data; //data数据
public static TreeMap<String, String> item(String value, String color) {
TreeMap<String, String> params = new TreeMap<String, String>();
params.put("value", value);
params.put("color", color);
return params;
}
@Override
public String toString() {
return "WechatTemplate" +
"{" +
"openId='" + touser + "'," +
"template_id='" + template_id + "'," +
"url='" + url + "'," +
"data=" + data +
'}';
}
}
public class WxConstant {
/**
* 开发者appId
*/
public static final String appId="xxxxxxxxxxxxxxxxx";
/**
* 开发者密码 appSecret
*/
public static final String appSecret="xxxxxxxxxxxxxxxxxxxxxxxx";
/**
* templateId:微信公众号模板id
*/
public static final String templateId="xxxxxxxxxxxxxxxxxxxx";
}
import com.alibaba.fastjson.JSONObject;
import java.util.HashMap;
import java.util.Map;
public class WxGetTokenUtil {
public static String getAccessToken() {
String appSecret = WxConstant.appSecret;
String appId = WxConstant.appId;
String tokenUrl = "https://api.weixin.qq.com/cgi-bin/token";
Map<String, String> params = new HashMap<>();
params.put("appid", appId);
params.put("secret", appSecret);
params.put("grant_type", "client_credential");
String response = HttpUtil.doGet(tokenUrl, params);
System.out.println("WxGetTokenUtil.getAccessToken()返回值:" + response);//此处可注释,调试时解开,防止code出报错找不到原因
JSONObject accessTokenObject = JSONObject.parseObject(response);
return accessTokenObject.getString("access_token");
}
}
import me.chanjar.weixin.mp.api.WxMpConfigStorage;
import me.chanjar.weixin.mp.api.WxMpInMemoryConfigStorage;
import me.chanjar.weixin.mp.api.WxMpService;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* wechat mp configuration
*/
@Configuration
@ConditionalOnClass(WxMpService.class)
public class WxMpConfiguration {
@Bean
@ConditionalOnMissingBean
public WxMpConfigStorage configStorage() {
WxMpInMemoryConfigStorage configStorage = new WxMpInMemoryConfigStorage();
configStorage.setAppId(WxConstant.appId);
configStorage.setSecret(WxConstant.appSecret);
// configStorage.setToken(WxConstant.token);
// configStorage.setAesKey(WxConstant.key);
return configStorage;
}
@Bean
@ConditionalOnMissingBean
public WxMpService wxMpService(WxMpConfigStorage configStorage) {
WxMpService wxMpService = new me.chanjar.weixin.mp.api.impl.WxMpServiceImpl();
wxMpService.setWxMpConfigStorage(configStorage);
return wxMpService;
}
}
import lombok.Data;
@Data
public class WxResult {
private String errcode;
private String errmsg;
private String msgid;
@Override
public String toString() {
return "WxError{errcode='" + errcode + "',errmsg='" + errmsg + "',msgid='" + msgid + "'}";
}
}
注意,参数是根据你自己的模板进行组装的,我的模板有以下参数
{first,keyword1,keyword2,keyword3,keyword4,keyword5,remark}
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.exception.WxErrorException;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.template.WxMpTemplateData;
import me.chanjar.weixin.mp.bean.template.WxMpTemplateMessage;
import net.sf.json.JSONObject;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TreeMap;
/**
* 推送公众号消息
*/
@Slf4j
@Component //使用方式2,需要用到这个注解,并且需要用到WxMapConfiguration。
public class WxSendUtil {
/**
* 发送消息,方式1
* @param openId 用户openId
* @param data 数据
* @param title 标题
*/
public static WxResult send(String openId, String data, String title) {
String url = "https://www.baidu.com";
String templateUrl = "https://api.weixin.qq.com/cgi-bin/message/template/send";
String time = parseDate3String(new Date());
String accessToken = WxGetTokenUtil.getAccessToken();
log.info("获取access_token,微信平台接口返回{}", accessToken);
TreeMap<String, TreeMap<String, String>> params = new TreeMap<String, TreeMap<String, String>>();
//根据具体模板参数的 添加数据
params.put("first", WeChatTemplate.item(title, "#000000"));//一般是标题
params.put("keyword1", WeChatTemplate.item("项目名称", "#ff0000"));//项目名称,字体颜色
params.put("keyword2", WeChatTemplate.item(time, "#000000"));
params.put("keyword3", WeChatTemplate.item(time, "#000000"));
params.put("keyword4", WeChatTemplate.item(time, "#000000"));
params.put("keyword5", WeChatTemplate.item(time, "#000000"));
params.put("remark", WeChatTemplate.item(data, "#000000"));
WeChatTemplate wechatTemplate = new WeChatTemplate();
wechatTemplate.setTemplate_id(WxConstant.templateId);//模板id
wechatTemplate.setTouser(openId);// 接收者openid
wechatTemplate.setUrl(url); // 模板跳转链接
wechatTemplate.setData(params);
JSONObject json = JSONObject.fromObject(wechatTemplate);//将java对象转换为json对象
String sendData = json.toString();//将json对象转换为字符串
log.info("板参数组装{}", sendData);
TreeMap<String, String> treeMap = new TreeMap<String, String>();
treeMap.put("access_token", accessToken);
String retInfo = HttpUtil.doPost(templateUrl, treeMap, sendData);
if (!StringUtils.isBlank(retInfo)) {
return JSON.parseObject(retInfo, WxResult.class);
}
log.info("消息模板返回{}", retInfo);
return null;
}
@Autowired
private WxMpService wxService;
/**
* 发送消息,方式2
* 发送微信模板信息
* @param openId openId
* @param url 跳转地址
* @param title 标题
*/
public Boolean sendWxMsg(String openId, String url, String title) {
String time = parseDate3String(new Date());
// 发送模板消息接口
WxMpTemplateMessage templateMessage = WxMpTemplateMessage.builder()
// 接收者openid
.toUser(openId)
// 模板id
.templateId(WxConstant.templateId)
// 模板跳转链接
.url(url)
.build();
// 添加模板数据
templateMessage.addData(new WxMpTemplateData("first", title, "#FF00FF"))
.addData(new WxMpTemplateData("keyword1", "紧急", "#A9A9A9"))
.addData(new WxMpTemplateData("keyword2", time, "#A9A9A9"))
.addData(new WxMpTemplateData("remark", url, "#FF00FF"));
String msgId = null;
try {
// 发送模板消息
msgId = this.wxService.getTemplateMsgService().sendTemplateMsg(templateMessage);
} catch (WxErrorException e) {
e.printStackTrace();
}
log.warn("·==++--·推送微信模板信息:{}·--++==·", msgId != null ? "成功" : "失败");
return msgId != null;
}
//时间转化成字符串,格式 yyyy-MM-dd HH:mm:ss
private static String parseDate3String(Date date) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return sdf.format(date);
}
}
发消息得有发送对象—用户,,要获取到用户的openId,然后才能进行发送
import com.alibaba.fastjson.JSONObject;
import com.sq.statistics.utils.wx.WxGetTokenUtil;
import com.sq.statistics.utils.wx.WxSendUtil;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/wx")
public class WXController {
@Autowired
private RestTemplate restTemplate;
/**
* 获取关注者的openid
*/
@GetMapping("/open-id/list")
public Map<String, Object> getOpenIdList(@RequestParam(required = false) String next_openid) {
String url = "https://api.weixin.qq.com/cgi-bin/user/get?access_token={access_token}";
Map<String, Object> param = new HashMap<>();
param.put("next_openid", next_openid);
param.put("access_token", WxGetTokenUtil.getAccessToken());
HttpHeaders headers = new HttpHeaders();
ResponseEntity<String> entity =
restTemplate.exchange(
url,
HttpMethod.GET,
new HttpEntity<String>(headers),
String.class,
param);
String result = entity.getBody();
System.out.println("WXController.getOpenIdList()返回值:" + result);//此处可注释,调试时解开,防止code出报错
if (result != null && result.contains("count")) {//返回值可能有两种,一种是报错的,一种是正常返回
Map<String, Object> resultData = JSONObject.parseObject(result, Map.class);
// return resultData;//可以直接返回,也可以自己抽取,,我只需要openId和next_openId
String next_openId = resultData.get("next_openid").toString(); //这个是下一组数据需要的传参
Map<String, Object> data = JSONObject.parseObject(resultData.get("data").toString(), Map.class);
List<String> openidList = JSONObject.parseArray(data.get("openid").toString(), String.class);
Map<String, Object> map = new HashMap<>();
map.put("next_openId", next_openId);
map.put("openidList", openidList);
return map;
}
return null;//返回的是tokenList,如果想要下一组数据,最后传的
/**
* 正确返回
* {"total":2,"count":2,"data":{"openid":["111111","222222"]},"next_openid":"222222"}
*
* 错误返回,下边这个是白名单没开
* {"errcode":40164,"errmsg":"invalid ip 180.0.0.0 ipv6 ::ffff:180.0.0.0, not in whitelist rid: 6268e703-5bc5bc49-4d17363f"}
*/
}
/**
* 发送消息
*/
@PostMapping("/send/message")
public void sendMessage(String message, String title, String openId) {
WxSendUtil.send(openId, message, title);
}
}