开篇整理
之前写过一篇关于微信/企业微信/钉钉授权登录的博文,阅读量还挺高,正好此次在工作中有需要微信公众号扫码登录的项目,利用工作间隙捋一下整个的代码开发流程,本文主要还是以后端JAVA为主,前端其实配合的地方不是很多主要是后端完成大部分的工作。
很多小伙伴其实对于微信公众号的开发文档看的很迷,功能写的很全,但如果第一次开发微信的功能多少都有点懵逼,哈哈哈,我就是懵逼的其中一员!
废话不多说接下来我们开始·····
Maven依赖
<!-- https://mvnrepository.com/artifact/com.github.binarywang/weixin-java-mp -->
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-mp</artifactId>
<version>4.1.0</version>
</dependency>
<dependency>
<groupId>com.github.liyiorg</groupId>
<artifactId>weixin-popular</artifactId>
<version>2.8.24</version>
</dependency>
配置公众测试号
- 配置信息修改,注意书写外网可访问接口即可
后续方法中使用的wxService
- 必要提醒:此处配置微信参数,配置好方法即可在后续方法中直接调用微信的WxMpService相当方便建议配置会省很多事情
/**
* @author hanfeng
* @date 2021/12/4
*/
@Component
public class WeChatMpConfig {
@Autowired
private SysConfigFileUrl configFileUrl;
@Bean
public WxMpService wxMpService(){
WxMpService wxMpService = new WxMpServiceImpl();
wxMpService.setWxMpConfigStorage(wxMpConfigStorage());
return wxMpService;
}
@Bean
public WxMpConfigStorage wxMpConfigStorage(){
WxMpDefaultConfigImpl wxMpDefaultConfig = new WxMpDefaultConfigImpl();
wxMpDefaultConfig.setAppId(configFileUrl.getMpAppId());
wxMpDefaultConfig.setSecret(configFileUrl.getMpAppSecret());
return wxMpDefaultConfig;
}
}
配置服务器所需接口
-
后端需写两个接口接受微信服务器的回调即上方/wechat/callback接口,名称无所谓只要外网能访问到就行。可参考微信开发文档配置地址微信官方接口说明如下:
-
服务器验证-具体实现逻辑代码如下(直接CV进行实验即可)注意:此处接受方法为GET上方图片有说明,所以此接口只是配合微信做一个服务器验证操作
/**
* 微信验证回调
*
* @param request
* @param response
*/
@GetMapping(value = "/callback")
public void init(HttpServletRequest request, HttpServletResponse response) {
log.info("==微信回调Token验证==");
try {
String nonce = request.getParameter("nonce");
String timestamp = request.getParameter("timestamp");
String signature = request.getParameter("signature");
String echoStr = request.getParameter("echostr");
//1.对三个字符进行排序
List<String> params = CollUtil.newArrayList(nonce, timestamp, #此处填写上方服务器配置所填写的Token);
Collections.sort(params);
//list转换成string
String tmpStr = ArrayUtil.toString(params);
//2.将三个参数字符串转换成字符串进行sha1加密
String sha1 = SecureUtil.sha1(tmpStr);
log.info("加密之前:" + signature + "加密之后" + sha1);
//3.比对两者的加密参数是否相等
if (sha1.equals(signature)) {
//4.验证成功返回给微信服务器消息验证
OutputStream outputStream = response.getOutputStream();
outputStream.write(URLUtil.encode(echoStr).getBytes());
outputStream.flush();
outputStream.close();
log.info("微信验证成功");
}
} catch (Exception e) {
e.printStackTrace();
}
}
- 服务器事件推送-微信官方说明如下:微信对应文档说明
- 接收微信服务器事件推送接口POST方法,完成此步骤即可实现接受微信的事件推送,具体思路根据微信回调的事件不同进行不同的处理,此处只是举例子不一定要写 注意:此方法为POST
/**
* 微信推送事件处理
*
* @param request
* @param response
*/
@PostMapping(value = "/callback")
public void Event(HttpServletRequest request, HttpServletResponse response) {
log.info("==微信推送事件回调处理==");
//依赖于dom4j 框架自带该依赖包springboot框架中
SAXReader saxReader = new SAXReader();
try {
BufferedReader reader = request.getReader();
org.dom4j.Document read = saxReader.read(reader);
Element rootElement = read.getRootElement();
Iterator iterator = rootElement.elementIterator();
Map<String, Object> map = new HashMap<>();
//此步骤主要为了拿到微信回调的xml参数进行map处理方便后续使用
while (iterator.hasNext()) {
Element element = (Element) iterator.next();
map.put(element.getName(), element.getStringValue());
}
//获取对应的参数值 需要看具体的字段对应的含义参考上方图片中有
String ticket = map.get("Ticket") + "";
log.info("ticket===" + ticket);
String eventType = map.get("Event") + "";
log.info("eventType===" + eventType);
String toUserName = map.get("ToUserName") + "";
log.info("toUserName===" + toUserName);
String fromUserName = map.get("FromUserName") + "";
log.info("fromUserName===" + fromUserName);
String createTime = map.get("CreateTime") + "";
log.info("createTime===" + createTime);
String msgType = map.get("MsgType") + "";
log.info("msgType===" + msgType);
//此步骤目前可不要 但后续扫码登录会用到,所以将ticket存储到redis中方便后续调用
//如未配置redis可暂时删除
/**if (StrUtil.isNotEmpty(ticket) && !"null".equals(ticket)) {
log.info(WX_TICKET_KEY_PREFIX + ticket);
redisService.setCacheMap(WX_TICKET_KEY_PREFIX + ticket, map);
redisService.expire(WX_TICKET_KEY_PREFIX + ticket,10L,TimeUnit.MINUTES);
}*/
//判断是否重复通知
if (wxMessageKey.exists(fromUserName + "-" + toUserName + "-" + createTime)) {
log.info(fromUserName+"微信消息通知重复");
return;
}
//如果为事件类型 VIEW 为授权登录无需发送消息通知
if ("event".equals(msgType)&&!"VIEW".equals(eventType)) {
List<WxMpKefuMessage> keFuMessages = new ArrayList<>();
/**处理订阅事件 即用户关注公众号操作*/
if("subscribe".equals(eventType)){
WxMpKefuMessage subscribeKeFuMessage = new WxMpKefuMessage();
subscribeKeFuMessage.setToUser(fromUserName);
subscribeKeFuMessage.setMsgType(WX_MESSAGE_TEXT);
subscribeKeFuMessage.setContent("欢迎来到CSDN \n\n工作时间:08:30 - 17:30");
keFuMessages.add(subscribeKeFuMessage);
} else if("unsubscribe".equals(eventType)){
//取消关注公众号目前不做记录
}
//扫码登录后发送客服消息提示文字
WxMpKefuMessage keFuMessage = new WxMpKefuMessage();
keFuMessage.setToUser(fromUserName);
keFuMessage.setMsgType(WX_MESSAGE_TEXT);
keFuMessage.setContent("您好,您已登录成功!");
keFuMessages.add(keFuMessage);
//处理取消订阅消息 此方法具体逻辑在下方
this.doSendKeFusMessage(keFuMessages);
log.info("eventType:" + eventType + ",fromUserName:" + fromUserName + ",toUserName:" + toUserName + ",msgType:" + msgType + ",createTime:" + createTime);
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 客服接口-发消息
*
* @param lstWxMpTemplate
*/
public void doSendKeFusMessage(List<WxMpKefuMessage> lstWxMpTemplate) {
WxMpKefuService kefuService = wxMpService.getKefuService();
for (WxMpKefuMessage news : lstWxMpTemplate) {
try {
boolean message = kefuService.sendKefuMessage(news);
log.info("发送客服消息结果:" + message);
} catch (Exception e) {
log.error(e.getMessage());
}
}
}
获取微信accessToken
- 此处获取accessToken方法具体如下:
@ApiOperation("获取token")
@GetMapping("/token")
public String accessToken() {
String accessToken = "";
try {
//此处依然为存入redis如不需要可直接忽略
/**Object token = redisService.getCacheObject(WX_TOKEN_KEY_PREFIX);
if (token != null) {
return token.toString();
}*/
//这里的accessToken与通过code获取的token不同需存储到缓存留着调用
WxMpApiUrl.Other getAccessTokenUrl = WxMpApiUrl.Other.GET_ACCESS_TOKEN_URL;
String url = String.format(
getAccessTokenUrl.getPrefix() + getAccessTokenUrl.getPath(),
wxMpConfigStorage.getAppId(),
wxMpConfigStorage.getSecret());
accessToken = HttpUtil.get(url);
JSONObject jsonObject = JSONUtil.parseObj(accessToken);
if (jsonObject.getStr("access_token") != null) {
accessToken = jsonObject.getStr("access_token");
/* redisService.setCacheObject(WX_TOKEN_KEY_PREFIX, accessToken, WX_EXPIRATION, TimeUnit.SECONDS);*/
}
log.info("获取token:" + accessToken);
} catch (Exception e) {
e.printStackTrace();
}
return accessToken;
}