首先登陆微信的公众号管理平台填写基本配置
根据上面的基本配置填写的校验的URL写对应的接口
@RestController
@RequestMapping("/officialAccounts")
public class officialAccounts extends BaseController {
@Value("${fanlyun.wx.appID}")
public String appID;
@Value("${fanlyun.wx.appsecret}")
public String appsecret;
private Logger log = LoggerFactory.getLogger(officialAccounts.class);
@Autowired
private WxOfficialCheckSignatureService wxOfficialCheckSignatureService;
/**
* @Description 验证消息的确来自微信服务器,签名验证
* @author lst
* @date 2020-8-18 12:03
* @return java.lang.String
*/
@RequestMapping(value = "/check-token", produces = "application/json; charset=utf-8")
public String checkToken(HttpServletRequest request , HttpSession session) {
String signature = request.getParameter("signature");
String timestamp = request.getParameter("timestamp");
String nonce = request.getParameter("nonce");
String echostr = request.getParameter("echostr");
String s = wxOfficialCheckSignatureService.checkSignature(signature, timestamp, nonce, echostr);
//接受微信的推送(关注和取消关注等)
try {
//解析微信返回的xml
Map<String, String> map = MessageUtil.parseXml(request);
//测试查看返回的消息
System.out.println(map.size());
for (String key : map.keySet()) {
if("Event".equals(key)){
if(map.get(key).equals("subscribe")){
//假如是关注事件
System.out.println("用户名为:"+map.get("FromUserName"));
wxOfficialCheckSignatureService.subscribe(map.get("FromUserName"));
break;
}
if(map.get(key).equals("unsubscribe")){
//假如是取消关注事件
wxOfficialCheckSignatureService.unsubscribe(map.get("FromUserName"));
break;
}
}
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return s ;
}
}
具体的实现类
@Service
public class WxOfficialCheckSignatureServiceImpl implements WxOfficialCheckSignatureService {
@Autowired
public RedisTemplate redisTemplate;
@Value("${fanlyun.wx.token}")
public String token;
@Value("${fanlyun.wx.getAccessTokenUrl}")
public String getAccessTokenUrl;
//公众号ID
@Value("${fanlyun.wx.appID}")
public String appID;
//小程序ID
@Value("${fanlyun.tencent.miniAppId}")
private String miniAppId;
@Value("${fanlyun.wx.appsecret}")
public String appsecret;
@Value("${fanlyun.wx.subscribeMessageUrl}")
private String subscribeMessageUrl;
@Autowired
private ISysWxUserInfoService sysWxUserInfoService;
@Autowired
private ISysUserService userService;
@Value("${fanlyun.wx.getPageUserInfoUrl}")
private String getPageUserInfoUrl;
private Logger log = LoggerFactory.getLogger(WxOfficialCheckSignatureServiceImpl.class);
@Override
public String checkSignature(String signature, String timestamp, String nonce, String echostr) {
// 1.将token、timestamp、nonce三个参数进行字典序排序
log.info("signature:{},token:{},timestamp:{},nonce:{}",signature,token,timestamp,nonce);
String tmpStr = ShaUtil.getSHA1(token, timestamp, nonce);
//TODO 进行对比
log.info("随机字符串echostr:{}",echostr);
log.info("tmpStr:{}",tmpStr);
if (tmpStr.equals(signature.toUpperCase())) {
return echostr;
}
return null;
}
/**
* 获取网页授权凭证
* @param appID 公众账号的唯一标识
* @param appsecret 公众账号的密钥
* @param code
* @return
*/
@Override
public Oauth2Token getOauth2Token(String appID, String appsecret, String code) {
log.info("获取网页授权凭证 开始...");
Oauth2Token auth = null;
//拼接请求地址
String requestUrl = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code";
requestUrl = requestUrl.replace("APPID", appID);
requestUrl = requestUrl.replace("SECRET", appsecret);
requestUrl = requestUrl.replace("CODE", code);
log.info("拼接后的请求地址为:" + requestUrl);
//获取网页授权凭证
com.alibaba.fastjson.JSONObject jsonObject = JSON.parseObject (HttpUtils.sendGet(requestUrl));
if(null != jsonObject) {
try {
auth = new Oauth2Token();
auth.setAccessToken(jsonObject.getString("access_token"));
auth.setExpiresIn(jsonObject.getInteger("expires_in"));
auth.setRefreshToken("refresh_token");
auth.setScope("scope");
auth.setOpenId(jsonObject.getString("openid"));
}catch(Exception e) {
auth = null;
int errorCode = jsonObject.getInteger("errcode");
String errorMsg = jsonObject.getString("errmsg");
log.error("获取网页授权凭证失败 errcode:{} errmsg:{}",errorCode,errorMsg);
}
}
log.info("获取网页授权凭证 结束...");
return auth;
}
// /**
// * 通过网页授权获取用户信息
// *
// * @param accessToken 网页授权接口调用凭证
// * @param openId 用户标识
// * @return
// */
// @Override
// public WxUserDO getWxUserInfo(String accessToken, String openId) {
// log.info("获取用户信息 开始...");
// log.info("网页授权接口访问凭证AccessToken:" + accessToken);
// log.info("用户标识openId:" + openId);
//
// WxUserDO wxUserInfo = null;
//
// // 拼接请求地址
// String requestUrl = "https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN";
// requestUrl = requestUrl.replace("ACCESS_TOKEN", accessToken);
// requestUrl = requestUrl.replace("OPENID", openId);
//
// log.info("拼接请求地址:" + requestUrl);
//
// // 通过网页授权获取用户信息
// com.alibaba.fastjson.JSONObject jsonObject = JSON.parseObject(HttpUtils.sendGet(requestUrl));
//
// if (null != jsonObject) {
// try {
// wxUserInfo = new WxUserDO();
// wxUserInfo.setOpenId(jsonObject.getString("openid"));
// wxUserInfo.setNickname(jsonObject.getString("nickname"));
// wxUserInfo.setSex(jsonObject.getInteger("sex"));
// wxUserInfo.setCountry(jsonObject.getString("country"));
// wxUserInfo.setProvince(jsonObject.getString("province"));
// wxUserInfo.setCity(jsonObject.getString("city"));
// wxUserInfo.setHeadimgurl(jsonObject.getString("headimgurl"));
// //用户授权信息
// List<String> list = JSON.parseArray(jsonObject.getString("privilege"), String.class);
// wxUserInfo.setPrivilegeList(list);
// //与开放平台公用的唯一标识,只有在用户讲公众号绑定到微信开放平台账号后,才会出现该字段
// wxUserInfo.setUnionid(jsonObject.getString("unionid"));
// }catch (Exception e) {
// wxUserInfo = null;
// int errorCode = jsonObject.getInteger("errcode");
// String errorMsg = jsonObject.getString("errmsg");
// log.error("获取用户信息失败 errcode:{} errmsg:{}",errorCode,errorMsg);
// }
// }
//
// log.info("获取用户信息 结束...");
// return wxUserInfo;
// }
/***
* 发送模板消息
* @param touser 接收者(用户)的 openid
* @param template_id 所需下发的订阅模板id
* @param pagepath 所需跳转到小程序的具体页面路径,支持带参数,(示例index?foo=bar),要求该小程序已发布,暂不支持小游戏
* @param data 模板内容,格式形如 { "key1": { "value": any }, "key2": { "value": any } }
* @param url 模板跳转链接(海外帐号没有跳转能力)
* @param color 模板内容字体颜色,不填默认为黑色
* @return
*/
@Override
public AjaxResult subscribeMessage(String accessToken, String touser, String template_id, String pagepath, Object data, String url,String color) {
Map<String, Object> map = new HashMap<>();
map.put("touser",touser);
map.put("template_id",template_id);
map.put("color",color);
map.put("data",data);
map.put("url",url);
if(StringUtils.isEmpty(pagepath)){
Map<String, Object> miniprogram = new HashMap<>();
miniprogram.put("appid",miniAppId);
miniprogram.put("pagepath",pagepath);
map.put("miniprogram",miniprogram);
}
String s = JSONUtils.toJSONString(map);
log.info("开始发送模板消息,body中的请求参数为:【{}】",s);
String result = HttpUtils.sendPostWithJson(subscribeMessageUrl+"?access_token="+accessToken, s);
Map<String,Object> jsonObject = JSON.parseObject(result);
if (jsonObject.get("errcode").toString().equals("0")) {
log.info("模板消息发送成功");
return AjaxResult.success("模板消息发送成功!");
}else {
log.info("模板消息发送失败,错误码为:{},错误信息为:{}",jsonObject.get("errcode").toString(),jsonObject.get("errmsg").toString());
return AjaxResult.error(jsonObject.get("errmsg").toString());
}
}
//发送模板消息
@Override
public void sendTemplateNews(SysNewTemplate sysNewTemplate,TmsNotice tmsNotice, SysUser sysUser){
Map<String, HashMap<String, String>> data = getData(tmsNotice,sysNewTemplate);
String accessToken = getToken();
AjaxResult ajaxResult = subscribeMessage(accessToken,sysUser.getOfficialOpenId(), sysNewTemplate.getWeixinTemplate(),"",data,"","");
log.info("发送模板消息后,返回的json为:"+ajaxResult);
}
/**
* 准备data里面的数据
* @param tmsNotice
* @param sysNewTemplate
* @return
*/
public Map<String, HashMap<String,String>> getData(TmsNotice tmsNotice, SysNewTemplate sysNewTemplate){
Map<String,HashMap<String,String>> mapTypes= new HashMap<String, HashMap<String, String>>();
mapTypes.put(sysNewTemplate.getWxTemplateField1(),new HashMap<String,String>() {
{
put("value", tmsNotice.getWxTemplateField1());
}
});
mapTypes.put(sysNewTemplate.getWxTemplateField2(),new HashMap<String,String>() {
{
put("value", tmsNotice.getWxTemplateField2());
}
});
mapTypes.put(sysNewTemplate.getWxTemplateField3(),new HashMap<String,String>() {
{
put("value", tmsNotice.getWxTemplateField3());
}
});
mapTypes.put(sysNewTemplate.getWxTemplateField4(),new HashMap<String,String>() {
{
put("value", tmsNotice.getWxTemplateField4());
}
});
mapTypes.put(sysNewTemplate.getWxTemplateField5(),new HashMap<String,String>() {
{
put("value", tmsNotice.getWxTemplateField5());
}
});
mapTypes.put(sysNewTemplate.getWxRemark(),new HashMap<String,String>() {
{
put("value", tmsNotice.getWxRemark());
}
});
return mapTypes;
}
/***
* 获取公众号全局唯一后台接口调用凭据(access_token)
* @return
*/
@Override
public String getToken(){
//先从Redis中获取Redis中没有则向微信端发送请求
Object o = redisTemplate.opsForValue().get(Constants.OFFICIAL_TOKEN);
if (o!=null) {
return o.toString();
}
log.info("======================开始获取公众号中的accessToken==========================");
String access_token = "";
String param = "grant_type=client_credential&appid=" + appID + "&secret=" + appsecret;
String str = HttpUtils.sendGet(getAccessTokenUrl, param);
Map<String, Object> mapTypes = JSON.parseObject(str);
for (Object obj : mapTypes.keySet()) {
if (mapTypes.containsKey("access_token")) {
access_token = mapTypes.get("access_token").toString();
redisTemplate.opsForValue().set(Constants.OFFICIAL_TOKEN, access_token, 3600, TimeUnit.SECONDS);
}
}
log.info("======================获取公众号中的accessToken完成,accessToken:【{}】==========================",access_token);
return access_token;
}
//关注事件
@Override
public void subscribe(String fromUserName) {
String token = getToken();
//拼接链接地址 https://api.weixin.qq.com/cgi-bin/user/info?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN
String url=getPageUserInfoUrl.replace("ACCESS_TOKEN",token);
url=url.replace("OPENID",fromUserName);
String str = HttpUtils.sendGet(url);
Map<String,Object> mapTypes = JSON.parseObject(str);
WxUserDO wxUserDO = new WxUserDO();
wxUserDO.setUnionid(mapTypes.get("unionid").toString());
List<WxUserDO> lists = sysWxUserInfoService.selectSysWxUserInfoList(wxUserDO);
//往微信公众号用户信息表中添加数据
wxUserDO.setOfficialOpenId(fromUserName);
wxUserDO.setCity(mapTypes.get("city").toString());
wxUserDO.setCountry(mapTypes.get("country").toString());
wxUserDO.setHeadimgurl(mapTypes.get("headimgurl").toString());
wxUserDO.setNickname(mapTypes.get("nickname").toString());
wxUserDO.setSex(Convert.toInt(mapTypes.get("sex")));
wxUserDO.setProvince(mapTypes.get("province").toString());
if(lists==null||lists.size()<=0){
sysWxUserInfoService.insertSysWxUserInfo(wxUserDO);
}else {
String openId = lists.get(0).getOpenId();
//往微信公众号用户信息表中更新数据
sysWxUserInfoService.updateByUnionid(wxUserDO);
userService.updateUserByOpenId(openId,wxUserDO.getOfficialOpenId());
}
}
//取关事件
@Override
public void unsubscribe(String fromUserName) {
}
}
将传送过来的参数排序
public class ShaUtil {
private static final Logger LOGGER = LoggerFactory.getLogger(ShaUtil.class);
/**
* @Description 用SHA1算法验证Token
* @author lst
* @date 2020-8-20 11:30
* @param token url相关的token
* @param timestamp 时间戳
* @param nonce 随机数
* @return java.lang.String
*/
public static String getSHA1(String token, String timestamp, String nonce){
String[] arr = new String[] { token, timestamp, nonce };
Arrays.sort(arr);
//TODO 2. 将三个参数字符串拼接成一个字符串进行sha1加密
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");
// 将三个参数字符串拼接成一个字符串进行sha1加密
byte[] digest = md.digest(content.toString().getBytes());
tmpStr = byteToStr(digest);
} catch (NoSuchAlgorithmException e) {
}
return tmpStr;
}
/**
* @Description 将字节数组转换为十六进制字符串
* @author lst
* @date 2020-8-18 11:56
* @param byteArray
* @return java.lang.String
*/
private static String byteToStr(byte[] byteArray) {
StringBuilder strDigest = new StringBuilder();
for (int i = 0; i < byteArray.length; i++) {
strDigest.append(byteToHexStr(byteArray[i]));
}
return strDigest.toString();
}
/**
* @Description 将字节转换为十六进制字符串
* @author lst
* @date 2020-8-18 11:57
* @param mByte
* @return java.lang.String
*/
private static String byteToHexStr(byte mByte) {
char[] Digit = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A',
'B', 'C', 'D', 'E', 'F' };
char[] tempArr = new char[2];
tempArr[0] = Digit[(mByte >>> 4) & 0X0F];
tempArr[1] = Digit[mByte & 0X0F];
String s = new String(tempArr);
return s;
}
}