微信公众号推送小程序模板消息的实现
准备:
微信小程序认证
微信公众号认证
微信开发平台认证(绑定微信小程序和公众号(unionid))
unionid:同一用户,对同一个微信开放平台下的不同应用,unionid是相同的
思路:
1. 获取小程序信息(用户openid小程序用户唯一ID)
将小程序与公众号绑定(绑定后才可获取unionid)
/**
* 小程序用户登录授权
*/
@GetMapping("/getUserOpenId")
public AjaxResult getUserOpenId(String code){
if (StringUtils.isEmpty(code)){
return AjaxResult.error("请输入code");
}
Map<String, Object> result = new LinkedHashMap();
String oAuth = WxAppletUserInfo.getOAuth(code, userAppId, userSecret);
JSONObject jsonObject = JSONObject.fromObject(oAuth);
//获取openId和sessionKey
String openid = jsonObject.get("openid").toString();
String session_key = jsonObject.get("session_key").toString();
String unionid= jsonObject.get("unionid").toString();
......
}
2.获取公众号的openid(公众号用户唯一ID)
--公众号回调
https://open.weixin.qq.com/connect/oauth2/authorize?appid=&redirect_uri=https%3A%2F%2Fty.breezedi.com%2Fadmin%2Fapi%2Fwx%2FscanQRCode&response_type=code&scope=snsapi_userinfo&state=0078d70e-68c#wechat_redirect
/**
* 微信授权回调
* @param request
* @param resp
* @throws IOException
*/
@RequestMapping(value = "/scanQRCode", method = {RequestMethod.GET,RequestMethod.POST})
public void scanQRCode(HttpServletRequest request,HttpServletResponse resp) throws IOException {
String code = request.getParameter("code");
if (null == code || code.trim().equals("")){
log.info("***********code为空************");
}
String state = request.getParameter("state");
if (null != state || !code.trim().equals("")){
log.info("state参数:{}",state);
}
//根据code获取token
String accessTokenCode = WxAppletUserInfo.getAccessTokenCode(code);
//获取openid
JSONObject jsonObject = JSONObject.fromObject(accessTokenCode);
log.info("微信回调后获取公众号参数:{}",jsonObject);
//获取openid和session_key
String openid = jsonObject.get("openid").toString();
String accessToken = jsonObject.get("access_token").toString();
//获取用户信息网页授权
String wxUserInfo = WxAppletUserInfo.getWxUserInfo(accessToken,openid);
log.info("用户信息{}",wxUserInfo);
JSONObject fromObject = JSONObject.fromObject(wxUserInfo);
String nickName = fromObject.getString("nickname");
String sex = fromObject.getString("sex");
String province = fromObject.getString("province");
String city = fromObject.getString("city");
String country = fromObject.getString("country");
String headImgUrl = fromObject.getString("headimgurl");
String privilege = fromObject.getString("privilege");
String unionId = fromObject.getString("unionid");
TyWxPatform patform = new TyWxPatform();
//授权判断用户是否关注公众号
//获取用户信息所需要的token
String token = WxAppletUserInfo.getAccessToken();
String infoSub = WxAppletUserInfo.getWxUserInfoSub(token, openid);
JSONObject json = JSONObject.fromObject(infoSub);
String subscribe = json.getString("subscribe");
//业务方法........
}
获取小程序公众号信息
package com.ty.common.wx;
import com.ty.common.utils.http.HttpUtils;
import lombok.extern.slf4j.Slf4j;
import net.sf.json.JSONObject;
import org.apache.commons.codec.binary.Base64;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.AlgorithmParameters;
import java.security.Key;
import java.security.Security;
@Slf4j
public class WxAppletUserInfo {
//获取小程序登录信息
private final static String GET_CODE_SESSION_URL="https://api.weixin.qq.com/sns/jscode2session";
// 算法名
private static final String KEY_NAME = "AES";
// ECB模式只用密钥即可对数据进行加密解密,CBC模式需要添加一个iv 加解密算法/模式/填充方式
private static final String CIPHER_ALGORITHM = "AES/CBC/PKCS7Padding";
//公众号AppID
private static final String appId = "";
//公众号秘钥
private static final String appSecret = "";
//通过code换取网页授权access_token
private static final String wxAuthLoginUrl = "https://api.weixin.qq.com/sns/oauth2/access_token";
//公众号网页登陆url
private static final String urlToken = "https://api.weixin.qq.com/cgi-bin/token";
//获取微信公众号信息--是否关注
private static final String userInfoSub = "https://api.weixin.qq.com/cgi-bin/user/info";
//获取微信公众号信息--网页授权 拉取用户信息
private static final String userInfo = "https://api.weixin.qq.com/sns/userinfo";
/**
* 获取小程序用户登陆信息
* @param code
* @param appId
* @param secret
* @return
*/
public static String getOAuth(String code,String appId,String secret) {
StringBuffer url = new StringBuffer(GET_CODE_SESSION_URL);
url.append("?appid="+appId).append("&secret="+secret)
.append("&js_code="+code)
.append("&grant_type=authorization_code");
return HttpUtils.sendGet(url.toString());
}
/**
* 微信 数据解密<br/>
* 对称解密使用的算法为 AES-128-CBC,数据采用PKCS#7填充<br/>
* 对称解密的目标密文:encrypted=Base64_Decode(encryptData)<br/>
* 对称解密秘钥:key = Base64_Decode(session_key),aeskey是16字节<br/>
* 对称解密算法初始向量:iv = Base64_Decode(iv),同样是16字节<br/>
*
* @param encryptedData 目标密文
* @param session_key 会话ID
* @param iv 加密算法的初始向量
*/
public static JSONObject wxDecrypt(String encryptedData, String session_key, String iv) {
log.info("手机号解密所需要的数据:\n{},\n{},\n{}\n",encryptedData,session_key,iv);
String json = null;
byte[] encrypted64 = Base64.decodeBase64(encryptedData);
byte[] key64 = Base64.decodeBase64(session_key);
byte[] iv64 = Base64.decodeBase64(iv);
byte[] data;
try {
init();
json = new String(decrypt(encrypted64, key64, generateIV(iv64)));
} catch (Exception e) {
log.error("*********解密微信手机号失败:**********" + e.getMessage());
}
return JSONObject.fromObject(json);
}
/**
* 初始化密钥
*/
public static void init() throws Exception {
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
KeyGenerator.getInstance(KEY_NAME).init(128);
}
/**
* 生成iv
*/
public static AlgorithmParameters generateIV(byte[] iv) throws Exception {
// iv 为一个 16 字节的数组,这里采用和 iOS 端一样的构造方法,数据全为0
// Arrays.fill(iv, (byte) 0x00);
AlgorithmParameters params = AlgorithmParameters.getInstance(KEY_NAME);
params.init(new IvParameterSpec(iv));
return params;
}
/**
* 生成解密
*/
public static byte[] decrypt(byte[] encryptedData, byte[] keyBytes, AlgorithmParameters iv)throws Exception {
Key key = new SecretKeySpec(keyBytes, KEY_NAME);
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
// 设置为解密模式
cipher.init(Cipher.DECRYPT_MODE, key, iv);
return cipher.doFinal(encryptedData);
}
/**
* 获取模板消息所需要的access_token(获取微信公众号的access_token(7200秒有效))
* @param
* @return
*/
public static String getAccessToken() {
StringBuffer url = new StringBuffer(urlToken);
url.append("?appid="+appId)
.append("&secret="+appSecret)
.append("&grant_type=client_credential");
String token = HttpUtils.sendGet(url.toString());
log.info("获取模板消息所的认证参数access_token:{}",token);
JSONObject Json = JSONObject.fromObject(token);
String access_token = String.valueOf(Json.get("access_token").toString());
return access_token;
}
/**
* 公众号
* 根据code获取到网页授权access_token
* @param code
* @return 微信回调后带有的参数code值
*/
public static String getAccessTokenCode(String code) {
StringBuffer url = new StringBuffer(wxAuthLoginUrl);
url.append("?appid="+appId)
.append("&secret="+appSecret)
.append("&code="+code)
.append("&grant_type=authorization_code");
log.info("根据code获取到网页授权access_token:{}",url.toString());
return HttpUtils.sendGet(url.toString());
}
/**
* 公众号
* 获取微信用户 (判断有无关注)获取用户信息
* @param accessToken
* @param openId
* @return
*/
public static String getWxUserInfoSub(String accessToken,String openId) {
StringBuffer url = new StringBuffer(userInfoSub);
url.append("?access_token="+accessToken)
.append("&openid="+openId)
.append("&lang=zh_CN");
log.info("获取微信用户信息(是否关注公众号):{}",url.toString());
return HttpUtils.sendGet(url.toString());
}
/**
* 公众号
* 获取微信用户--网页授权获取用户信息
* @param accessToken
* @param openId
* @return
*/
public static String getWxUserInfo(String accessToken,String openId) {
StringBuffer url = new StringBuffer(userInfo);
url.append("?access_token="+accessToken)
.append("&openid="+openId)
.append("&lang=zh_CN");
String urlUserInfo = url.toString();
log.info("获取微信用户信息url:{}",urlUserInfo);
return HttpUtils.sendGet(urlUserInfo);
}
}
HTTPUtils
/**
*
* @Title: get
* @Description: TODO(get请求)
* @param @param url
* @param @return 设定文件
* @return String 返回类型
* @throws
*/
public static String sendGet(String url) {
String result = "";
// 设置请求配置
RequestConfig globalConfig = RequestConfig.custom().setCookieSpec(CookieSpecs.STANDARD)
.setConnectionRequestTimeout(10000).setConnectTimeout(10000).setSocketTimeout(50000). build();
// 打开浏览器
CloseableHttpClient httpClient = HttpClients.custom().setDefaultRequestConfig(globalConfig).build();
HttpGet httpGet = new HttpGet(url);
RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(30000).setConnectionRequestTimeout(30000).setSocketTimeout(30000).build();
httpGet.setConfig(requestConfig);
httpGet.addHeader("Content-type", "application/json;charset=utf-8");
httpGet.setHeader("Accept", "application/json");
// 设置头
httpGet.addHeader("User-Agent",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.152 Safari/537.36");
httpGet.addHeader("Cookie", "_gat=1; nsfw-click-load=off; gif-click-load=on; _ga=GA1.2.1861846600.1423061484");
// 收到回复
CloseableHttpResponse response;
try {
response = httpClient.execute(httpGet);
log.info("HTTPGet请求状态,status:{}" , response.getStatusLine().getStatusCode());
result = EntityUtils.toString(response.getEntity(), "utf-8");
EntityUtils.consume(response.getEntity());
response.close();
httpGet.releaseConnection();
httpClient.close();
} catch (ClientProtocolException e) {
log.info("发送GET请求出现异常!" + e);
e.printStackTrace();
} catch (IOException e) {
log.info("发送GET请求出现异常!" + e);
e.printStackTrace();
}
return result;
}
3.推送消息服务
公众号模板申请:一个月只能有三次,审核7-15个工作日
//1.获取access_token
https请求方式: GET
urlToken = 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET'
String TemplateId = "";
//2.模板消息类
//openid 是用户公众号的openid
public static JSONObject packJsonDoctorMsg(String openid,String AppId,String pagePath){
//医生发送公众号模板消息
WechatTemplate wechatTemplate = new WechatTemplate();
wechatTemplate.setTouser(openid);
wechatTemplate.setTemplate_id(TemplateId);
MiniprogramData miniprogramData = new MiniprogramData();
miniprogramData.setAppid(AppId);
miniprogramData.setPagepath(pagePath);//所需跳转到小程序的具体页面路径,支持带参数,(示例index?foo=bar),要求该小程序已发布,暂不支持小游戏
wechatTemplate.setMiniprogram(miniprogramData);
Map<String,TemplateData> dataMap = new HashMap<>();
TemplateData title = new TemplateData();
title.setValue("所发送消息内容");
title.setColor("#000000");
dataMap.put("title",title);
TemplateData first = new TemplateData();
first.setValue("所发送消息内容";
first.setColor("#000000");
dataMap.put("first",first);
TemplateData keyword = new TemplateData();
keyword.setValue("所发送消息内容");
keyword.setColor("#000000");
dataMap.put("keyword",keyword);
TemplateData keyword = new TemplateData();
keyword.setValue("所发送消息内容");
keyword.setColor("#000000");
dataMap.put("keyword", keyword);
TemplateData keyword = new TemplateData();
keyword.setValue("所发送消息内容");
keyword.setColor("#000000");
dataMap.put("keyword", keyword);
TemplateData keyword = new TemplateData();
keyword.setValue("所发送消息内容");
keyword.setColor("#000000");
dataMap.put("keyword", keyword);
TemplateData remark = new TemplateData();
remark.setValue("所发送消息内容");
remark.setColor("#000000");
dataMap.put("remark", remark);
wechatTemplate.setData(dataMap);
JSONObject json = JSONObject.fromObject(wechatTemplate);
return json;
}
//3.用户发送模板消息
JSONObject userMsgDate = MessageModel.packJsonUserMsg(openid, AppId, userPagePath);
JSONObject jsonobjUser = HttpUtils.httpRequest(send+WxAppUserInfo.getAccessToken(urlToken,appId,appSecret),"POST",userMsgDate.toString());
over~