项目系统JasmineProject集成了微信扫码功能,在实现的过程中遇到了一些坑, 经过不断尝试,最后实现了此功能,梳理了下大概流程逻辑。
预先处理
- 已注册有微信公众号。
- 在微信公众号管理后台可以获取到appId和secrect。
- 微信消息接口已打通,如何接收微信消息自行查阅,提两点注意的坑。
- 微信接收消息和校验token为同一地址,get方法为校验token,post方法为消息通知。
- 配置的通知地址不要漏写 结尾的"/",否则接收不到消息。
生成二维码
为前端提供一个生成二维码的接口
/**
* 生成公众号二维码
*
* @return {ticket,sceneStr}
*/
@NoTokenCheck
@GetMapping("wechatQrCode")
public R<CreateQrScanResp> getQrScan() {
CreateQrScanResp result = WechatHaoUtil.createQrCode();
return responseData(result);
}
- 获取access token 参考文档 微信开放文档
- 后台生成一个sceneStr(uuid或者其他),使用sceneStr调用微信接口生成二维码 ,将sceneStr和ticket返回给前端, 参考文档微信开放文档。
- 前端使用ticket获取 二维码并展示 ,获取图片 地址为https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=${ticket} ,前端记录sceneStr,后续有用
- sceneStr 作为登录后的 token的key
之后前端的处理逻辑,有两种方式
轮询方式
- 前端使用sceneStr轮询后台接口(5秒1次),只要轮询到token,即登录成功。
- 使用sceneStr 轮询token
/**
* 轮询检查 SceneStr 登陆结果
*
* @param sceneStr
* @return
*/
@NoTokenCheck
@GetMapping("getTokenBySceneStr")
public R<String> getTokenBySceneStr(@NotBlank String sceneStr) {
String token = CodeCache.WECHAT_LOGIN_SCENE_STR_CACHE.get(null, String.class, sceneStr);
if (token == null) {
token = "";
}
return responseData(token);
}
-
使用token完成后续逻辑
实时通知
- 后台通过webSocket的方式,通知前端登录结果,由于需要额外增加webSocket功能,我们没有使用这种方式
用户扫码
- 用户使用微信扫码
- 后台收到微信扫码通知消息,处理逻辑 ,微信通知消息文档 关注/取消关注事件 | 微信开放文档
/**
* 登录
*
* @param wechatMessage
* @return
*/
private String processLogin(WechatMessage wechatMessage) {
String sceneStr = wechatMessage.getEventKey();
// 已绑定直接登录
if (userWechatService.isOpenIdBind(wechatMessage.getFromUserName())) {
// open id 登录
WechatLoginReq wechatLoginReq = new WechatLoginReq();
wechatLoginReq.setSceneStr(sceneStr);
wechatLoginReq.setOpenId(wechatMessage.getFromUserName());
userService.loginByWechat(wechatLoginReq);
WechatMessage reply = WechatMessage.builder()
.CreateTime(System.currentTimeMillis() / 1000)
.FromUserName(wechatMessage.getToUserName())
.ToUserName(wechatMessage.getFromUserName())
.MsgType(WeChatMessageType.TEXT)
.Content("登录成功,2s后自动跳转")
.MsgId(CommonUtil.uuid())
.build();
String msg = XmlUtil.toXml(reply);
return msg;
}
// 未绑定 发送绑定页面消息
WechatMessage reply = WechatMessage.builder()
.CreateTime(System.currentTimeMillis() / 1000)
.FromUserName(wechatMessage.getToUserName())
.ToUserName(wechatMessage.getFromUserName())
.MsgType(WeChatMessageType.TEXT)
.Content("您的账户尚未绑定,点击绑定账户菜单完成账户绑定,并重试")
.MsgId(CommonUtil.uuid())
.build();
String msg = XmlUtil.toXml(reply);
return msg;
}
openId 已绑定
- 通过openId绑定获取到我们系统的用户信息,完成登录。
- 设置 seceneStr- token 缓存,等待前端轮询,后台处理逻辑结束,回复消息登陆成功。
openId 未绑定
- 回复消息提醒用户绑定,我们尝试过回复一个文章链接消息(跳转绑定页面),但是貌似微信无法打开此链接,遂采用自定义菜单的方式进行绑定。参考文档 网页授权 | 微信开放文档
- 用户点击绑定菜单,获取用户授权并跳转到绑定页面
- 自定义菜单的跳转链接为
其中redirect_uri 为授权成功后的回调地址,注意需要urlEncode ,可以附带参数,但需要吧 '&','?'等符号转义。用户授权成功后会附带一个code参数到回调页面,位于queryString上。
- 绑定页面逻辑处理
- 我们使用登录页面处理绑定逻辑,并开发新的页面。
- 检查到queryString包含code参数,即为绑定逻辑,否则为正常登录逻辑。
- 用户登录成功后,使用code 和 token 完成微信用户绑定。
/**
* 获取微信用户信息然后注册
*
* @param weChatRegisterReq {code,token}
* @return
*/
@PostMapping("registerWechat")
public R registerWechat(@RequestBody WeChatRegisterReq weChatRegisterReq) {
wechatService.registerWechat(weChatRegisterReq);
return success();
}
- 调用网页授权接口获取微信用户信息,使用token获取到我们系统的用户信息,完成绑定
@Override
public String registerWechat(WeChatRegisterReq weChatRegisterReq) {
// 通过code 获取用户信息
WechatUserInfoResp wechatUserInfoResp = WechatHaoUtil.getUserInfoByCode(weChatRegisterReq.getCode());
UserWechatAddReq userWechatAddReq = UserWechatAddReq.builder()
.openId(wechatUserInfoResp.getOpenId())
.nickName(wechatUserInfoResp.getNickname())
.avatar(wechatUserInfoResp.getHeadImageUrl())
.city(wechatUserInfoResp.getCity())
.province(wechatUserInfoResp.getProvince())
.country(wechatUserInfoResp.getCountry())
.sex(wechatUserInfoResp.getSex())
.unionId(wechatUserInfoResp.getUnionId())
.build();
UserWechat userWechat = userWechatService.addOrUpdate(userWechatAddReq);
return userWechat.getOpenId();
}
参考截图
- 微信登录按钮
- 展示二维码
- 绑定菜单
- 用户扫描后回复消息
- 轮询到token后进入系统
JasmineProject 专业的项目管理工具,欢迎大家注册使用!