1、微信公众号配置类,采用springboot配置文件存储相关信息和密钥
@ConfigurationProperties(prefix = "wx.offiaccount")
@Component
@Slf4j
public class WxConfig {
private String appSecret;//开发者密码(AppSecret)
private String appId;//开发者ID(AppID)
private String token;//令牌(Token)
private String accessToken;
private List<String> ipList;
@Autowired
private RestTemplate restTemplate;
@PostConstruct
public void init(){
refreshAccessToken();
refreshIPAddress();
}
public void refreshAccessToken(){
String url = "https://api.weixin.qq.com/cgi-bin/token?";
String grant_type = "client_credential";
ResponseEntity<Map> responseEntity = restTemplate.getForEntity(url + "grant_type=" + grant_type + "&appid=" + appId + "&secret=" + appSecret, Map.class);
Map body = responseEntity.getBody();
accessToken = (String) body.get("access_token");
log.info("accessToken refresh:{}",accessToken);
}
public void refreshIPAddress(){
String url = "https://api.weixin.qq.com/cgi-bin/getcallbackip?";
ResponseEntity<Map> res = restTemplate.getForEntity(url + "access_token=" + accessToken, Map.class);
Map body = res.getBody();
List<String> ips = (List<String>) body.get("ip_list");
log.info("ip address:{}",ips);
ipList = ips;
}
public void setAppSecret(String appSecret) {
this.appSecret = appSecret;
}
public void setAppId(String appId) {
this.appId = appId;
}
public void setToken(String token) {
this.token = token;
}
public void setAccessToken(String accessToken) {
this.accessToken = accessToken;
}
public String getAppSecret() {
return appSecret;
}
public String getAppId() {
return appId;
}
public String getToken() {
return token;
}
public String getAccessToken() {
return accessToken;
}
public List<String> getIpList() {
return ipList;
}
public void setIpList(List<String> ipList) {
this.ipList = ipList;
}
}
accessToken有效期两个小时,项目启动后用@PostConstruct注解实现启动后自动执行初始化,可以用定时任务刷新,回调ip同理
2、接收微信回调消息,微信回调采用xml格式数据
@ApiOperation("接收微信返回消息")
@RequestMapping(value = "/getMsg",method = {RequestMethod.GET,RequestMethod.POST})
public String msg(@RequestParam Map<String,String> param, HttpServletRequest request) throws IOException, DocumentException {
String ip = ServletUtil.getClientIP(request);
if (!wxConfig.getIpList().contains(ip)){
log.info("非法IP:{}",ip);
return "回调IP非法";
}
ServletInputStream inputStream = request.getInputStream();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
String s = null;
StringBuffer xmlData = new StringBuffer();
while ((s = bufferedReader.readLine()) != null ){
xmlData = xmlData.append(s);
}
log.info("微信消息:{}",param);
log.info("微信推送:{}",xmlData);
Document document = DocumentHelper.parseText(xmlData.toString());
Element rootElement = document.getRootElement();
Iterator iterator = rootElement.elementIterator();
HashMap<String, String> msg = new HashMap<>();
while (iterator.hasNext()){
Element element = (Element) iterator.next();
log.info("解析xml参数:key=>{},value=>{}",element.getName(),element.getTextTrim());
msg.put(element.getName(),element.getTextTrim());
}
log.info("解析xml参数map:{}",msg);
String openId = msg.get("FromUserName");
String event = msg.get("Event");
String eventKey = msg.get("EventKey");
//关注行为 排除非扫码关注的情况:无eventKey
if (WXEventEnum.SUBSCRIBE.getValue().equals(event) && StringUtils.isNotEmpty(eventKey)){
String content = "公众号关注成功";
wxApi.sendMsg(content,openId);
//设置关注后的状态为1
LambdaQueryWrapper<UserEntity> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(UserEntity::getOpenId,openId);
int count = authService.count(queryWrapper);
if (count > 0){
LambdaUpdateWrapper<UserEntity> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.eq(UserEntity::getOpenId,openId).set(UserEntity::getWxSubscribe,BooleanEnum.TRUE.getCode());
authService.update(updateWrapper);
}
redisTemplate.opsForValue().set(eventKey, openId, ConstantPool.QRSCENE_TIME, TimeUnit.MINUTES);
}
//扫码行为
if (WXEventEnum.SCAN.getValue().equals(event)){
redisTemplate.opsForValue().set(ConstantPool.QRSCENE + eventKey, openId, ConstantPool.QRSCENE_TIME, TimeUnit.MINUTES);
}
//取关 清除openId
if (WXEventEnum.UNSUBSCRIBE.getValue().equals(event)){
LambdaUpdateWrapper<UserEntity> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.eq(UserEntity::getOpenId,openId).set(UserEntity::getWxSubscribe,BooleanEnum.FALSE.getCode());
authService.update(updateWrapper);
}
//1)将token、timestamp、nonce三个参数进行字典序排序
// 微信加密签名,signature结合了开发者填写的 token 参数和请求中的 timestamp 参数、nonce参数。
//timestamp 时间戳
//nonce 随机数
//echostr 随机字符串
// String timestamp = param.get("timestamp");
// String nonce = param.get("nonce");
// String signature = param.get("signature");
// String echostr = param.get("echostr");
// ArrayList<String> list = new ArrayList<>();
// list.add(wxConfig.getToken());
// list.add(timestamp);
// list.add(nonce);
// log.info("排序前{}",list);
// Collections.sort(list);
// //2)将三个参数字符串拼接成一个字符串进行sha1加密
// String ecryptStr = DigestUtils.sha1Hex(list.get(0) + list.get(1) + list.get(2));
// log.info("加密后参数:{}",ecryptStr);
// //3)开发者获得加密后的字符串可与 signature 对比,标识该请求来源于微信signature
// if (StringUtils.equals(ecryptStr,signature)) {
// log.info("参数相同");
// return echostr;
// }
return null;
}
消息接收枚举类
public enum WXEventEnum implements BaseEnum{
SUBSCRIBE(0,"subscribe"),//订阅
UNSUBSCRIBE(1,"unsubscribe"),//取消订阅
SCAN(2,"SCAN") //扫码
;
private int code;
private String value;
WXEventEnum(int code, String value) {
this.code = code;
this.value = value;
}
public String getValue(){
return this.value;
}
@Override
public int getCode() {
return this.code;
}
}