使用前提
有一个认证过的微信公众号(服务号)
解决思路
使用生成带参数的二维码,微信事件通知来解决这个问题
具体要实现的流程,类似于Tower的扫码关注公众号并接收消息通知
点击绑定二维码,弹出对用公众号二维码图片
使用微信扫码
点击关注后,成功绑定微信公众号。并能接收公众号推送的消息。
以上就是需要实现的功能。
具体实现
其实微信挺坑的
配置公众号
开发->基本配置
获取AppID AppSecret ,配置白名单
设置与开发->公众号设置->功能设置
配置网页授权域名
开发->基本配置->服务器配置
注:提交该配置服务器必须要有验证此token的接口(接收事件通知的接口->就是你配置的服务器地址(URL),token是自定义的填啥都行)
目前使用的是明文模式,后续有机会更新为加密模式。
配置事件通知接口(类似于微信支付回调地址一样,接收微信官方给推送的事件消息)
服务->模版消息
如果没看到模版消息,就把左侧菜单栏拉到底,找见模版消息 加到菜单里就行了
根据也无需求增加一个你要用的模版
Java代码编写
导入一个微信开发工具包SpringBoot版(神器)
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>wx-java-mp-spring-boot-starter</artifactId>
<version>4.1.0</version>
</dependency>
yaml文件配置
公众号配置
mp:
app-id: #AppId
secret: #AppSecret
token: #自定义的token
aes-key: #消息加解密秘钥(EncodingAESKey)
获取服务器的二维码
// 参数userId可视为附加值,因为我需要通过userId来将用户openId关联
// 注入
private final WxMpService wxMpService;
/**
* @Description 获取服务号二维码(带参)
* @author Rick Jen
* @Date 2021/8/17 21:47
*/
@RequestMapping(value = "/getMpQrCode",method = RequestMethod.GET)
public ApiResult getMpQrCode(String userId) throws WxErrorException {
// 先获取ticket
WxMpQrCodeTicket wxMpQrCodeTicket = wxMpService.getQrcodeService().qrCodeCreateLastTicket(userId);
// 通过ticket获取微信生成的二维码链接
return ok(wxMpService.getQrcodeService().qrCodePictureUrl(wxMpQrCodeTicket.getTicket()));
}
服务器地址(URL)接口编写
| 参数 | 描述 |
|---|---|
| ToUserName | 开发者微信号 |
| FromUserName | 发送方帐号(一个OpenID) |
| CreateTime | 消息创建时间 (整型) |
| MsgType | 消息类型,event |
| Event | 事件类型,subscribe |
| EventKey | 事件KEY值,qrscene_为前缀,后面为二维码的参数值 |
| Ticket | 二维码的ticket,可用来换取二维码图片 |
// 此接口的uri必须和你配置的那个服务器地址(URL)对应上 注意放行此API
// 注入
private final WxMpService wxMpService;
/**
* @Description 验证token 获取时间通知信息
* @author Rick Jen
* @Date 2021/8/17 17:42
*/
@RequestMapping(value = "/redirectUri")
public String redirectUri(HttpServletRequest request, HttpServletResponse response) throws WxErrorException, IOException {
ServletInputStream inputStream = request.getInputStream();
ServletOutputStream outputStream = response.getOutputStream();
String signature = request.getParameter("signature");
String timestamp = request.getParameter("timestamp");
String nonce = request.getParameter("nonce");
String echostr = request.getParameter("echostr");
String token = wxMpService.getWxMpConfigStorage().getToken();
String xmlResult = IOUtils.toString(request.getInputStream(), request.getCharacterEncoding());
WxMpXmlMessage wxMpXmlMessage = WxMpXmlMessage.fromXml(xmlResult);
log.info("接收到微信回调消息{{}}" , wxMpXmlMessage.toString());
if (StringUtils.isNotBlank(wxMpXmlMessage.getEventKey())){
// 附加值
String[] eventKeyStr = wxMpXmlMessage.getEventKey().split("_");
// 用户ID
String userId = eventKeyStr[1];
// 关注公众号
if (wxMpXmlMessage.getEvent().equals("subscribe")){
//TODO 执行你的操作
}else {
//TODO 取消关注
}
}
//排序
String[] arr = {token, timestamp, nonce};
Arrays.sort(arr);
StringBuilder content = new StringBuilder();
for (String s : arr) {
content.append(s);
}
//sha1Hex 加密
MessageDigest md = null;
String temp = null;
try {
md = MessageDigest.getInstance("SHA-1");
byte[] digest = md.digest(content.toString().getBytes());
temp = byteToStr(digest);
log.info("加密后的token:" + temp);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
assert temp != null;
if ((temp.toLowerCase()).equals(signature)) {
return echostr;
}
return null;
}
private static String byteToStr(byte[] byteArray) {
String strDigest = "";
for (int i = 0; i < byteArray.length; i++) {
strDigest += byteToHexStr(byteArray[i]);
}
return strDigest;
}
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;
}
发送消息
// openId为用户在此微信公众号的唯一标识(同一用户在不同场景下的openId都不一样)
// 注入
private final WxMpService wxMpService;
/**
* @Description 发送消息
* @author Rick Jen
* @Date 2021/8/17 21:52
*/
@RequestMapping(value = "/sendTemplateMessage", method = RequestMethod.POST)
public ApiResult sendTemplateMessage(String openId) {
if (StringUtils.isBlank(openId)){
return fail("openID为空!");
}
log.info("获取到参数openID{{}}" , openId);
// 需要跳转的小程序及页面
WxMpTemplateMessage.MiniProgram miniProgram = new WxMpTemplateMessage.MiniProgram();
miniProgram.setAppid("小程序的openId");
// 注意如果小程序对应页面要token 这个必须设置为true 否则报错{“errcode“:40165,“errmsg“:“invalid weapp pagepath}
miniProgram.setUsePath(true);
miniProgram.setPagePath("要跳转的小程序页面-可以携带参数");
// 创建消息模板对象
WxMpTemplateMessage templateMessage = WxMpTemplateMessage.builder()
.toUser(openId)
.templateId("模版ID")
.miniProgram(miniProgram)
.build();
// 填写模板消息及内容 若有多个就再往下加就行了
templateMessage.addData(new WxMpTemplateData("first", "值"));
templateMessage.addData(new WxMpTemplateData("keyword1", DateUtil.today()));
try {
// 发送消息 并返回消息ID
String messageId = wxMpService.getTemplateMsgService().sendTemplateMsg(templateMessage);
log.info("发送模版消息后的消息id:{{}}" , messageId);
return ok("消息成功!");
} catch (WxErrorException e) {
e.printStackTrace();
}
return fail("消息发送失败!");
}
至此结束。完成了从用户扫码到发送消息的一个功能。
在此之前还走出了一条实现了功能,但是没有这个完善的路。有兴趣可以继续往下看
具体实现
思路:通过微信的静默授权来获取用户openId。将授权接口地址生成一个二维码,告诉微信用户走了这个地址后回调到我们服务器的那个接口,用户微信扫描二维码进行重定向并获取用户的openId。
公众号配置
这个只需要配置一个白名单和一个安全域名即可。
代码实现
maven依赖导入
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>wx-java-mp-spring-boot-starter</artifactId>
<version>4.1.0</version>
</dependency>
<!--HuTool工具类依赖 https://hutool.cn/docs/#/ -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.7.9</version>
</dependency>
<!--生成二维码依赖-->
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>core</artifactId>
<version>3.3.3</version>
</dependency>
获取授权二维码
// 注入
private final WxMpService wxMpService;
/**
* @Description 静默重定向获取用户openId地址
* @author Rick Jen
* @Date 2021/8/17 22:08
*/
@RequestMapping(value = "/getMpQrCode",method = RequestMethod.GET)
public ApiResult getMpQrCode(){
// 确定自己的回调地址
String redirectUri = "回调通知地址";
String url = wxMpService.getOAuth2Service().buildAuthorizationUrl(redirectUri,"snsapi_base","附加值");
// 将这个url生成一个二维码
QrConfig qrConfig = new QrConfig();
// 想弄好看点自己设置
qrConfig.setWidth(300);
qrConfig.setHeight(300);
String base64Qrcode = QrCodeUtil.generateAsBase64(url, qrConfig, "jpg");
return ok(base64Qrcode);
}
用户扫码后微信回调通知接口
/**
* @Description 静默授权重定向接收通知
* @author Rick Jen
* @Date 2021/8/17 20:52
*/
@Deprecated
@RequestMapping(value = "/notifyUrl")
public void notifyUrl(HttpServletRequest request) throws WxErrorException {
// 微信返回code
String code = request.getParameter("code");
log.info("返回参数code{{}}", code);
// 获取附加值
String state = request.getParameter("state");
log.info("附带参数{{}}", state);
// 通过code获取微信access_token
WxOAuth2AccessToken wxOAuth2AccessToken = wxMpService.getOAuth2Service().getAccessToken(code);
// 获取该code对应的用户openId
String openId = wxOAuth2AccessToken.getOpenId();
log.info("用户openId{{}}", openId);
//TODO 执行你自己的操作
}
发送模版消息和上面一样
这种操作也可以实现此功能,唯一不好的一点就是通过用户扫码之后白屏了。后面尝试生成一个此微信公众号的二维码,然后解析该二维码得到一个地址,让跳转到得到的那个地址,结果一样,也是白屏。达不到想要的效果,所以尝试使用接收微信消息通知这个功能来实现了。
至此就全部结束了。
微信虽然很坑,但是功能很全。你想要的逻辑他基本都有,就看你怎么去理解文档了。