一、背景
小程序可以通过微信官方提供的登录能力方便地获取微信提供的用户身份标识,快速建立小程序内的用户体系。
微信小程序官方文档:https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/login.html
二、技术栈
- SpringBoot 2.0
- MyBatis-Plus
- MySQL
- Redis
- Sa-Token
- weixin-java-miniapp
三、第一个小程序
参考微信步骤:https://developers.weixin.qq.com/miniprogram/dev/framework/quickstart/getstart.html#%E7%94%B3%E8%AF%B7%E8%B4%A6%E5%8F%B7
四、登录时序图
五、实现
5.1 登录
5.1.1 wx.login获取code登录
onLaunch(options) {
wx.login({
success: function (res) {
if (res.code) {
// 发起网络请求获取 session_key 和 openid
wx.request({
url: "http://localhost:8087/auth/login?code=" + res.code,
method: 'POST',
success: function (response) {
if (response.data.code === 200) {
const token= response.data.data;
wx.setStorageSync('token', token);
} else {
console.log("登录失败")
}
},
fail: function (e) {
}
});
}
}
});
},
5.1.2 后端实现
private final WxMaService wxMaService;
private final RedisTemplate<String, Object> redisTemplate;
@ApiOperation("微信登录")
@PostMapping(value = "/login")
public ResultVO<String> login(@Valid @NotBlank(message = "微信code不能为空") String code) throws WxErrorException {
WxMaJscode2SessionResult sessionInfo = wxMaService.getUserService().getSessionInfo(code);
if (null == sessionInfo) {
return ResultVO.fail("获取session失败");
}
// todo 模拟通过openid查询用户
String openid = sessionInfo.getOpenid();
Long userId = 1L;
// 登录
StpUtil.login(userId);
// token
String tokenValue = StpUtil.getTokenValue();
// 缓存用户信息
UserLoginVO loginVO = new UserLoginVO();
loginVO.setId(userId);
loginVO.setWxOpenId(openid);
loginVO.setToken(tokenValue);
redisTemplate.opsForValue().set("userinfo:" + userId, JSONObject.toJSONString(loginVO));
return ResultVO.success(tokenValue);
}
5.2 获取头像
<button class="avatar-wrapper" open-type="chooseAvatar" bind:chooseavatar="onChooseAvatar">
<image class="avatar" src="{{avatarUrl}}"></image>
</button>
Page({
data: {
avatarUrl: 'https://mmbiz.qpic.cn/mmbiz/icTdbqWNOwNRna42FI242Lcia07jQodd2FJGIYQfG0LAJGFxM4FbnQP6yfMxBgJ0F3YRqJCJ1aPAK2dQagdusBZg/0'
},
onLoad: function (options) {
// 页面创建时执行
},
onShow: function () {
// 页面出现在前台时执行
},
onReady: function () {
// 页面首次渲染完毕时执行
},
onHide: function () {
// 页面从前台变为后台时执行
},
onUnload: function () {
// 页面销毁时执行
},
onChooseAvatar(e) {
const {avatarUrl} = e.detail
this.setData({
avatarUrl,
});
// todo 上传文件
}
})
5.3 获取昵称
<input model:value="{{nickName}}" bindinput="bindKeyInput" type="nickname" class="weui-input" placeholder="请输入昵称"/>
Page({
data: {
nickName: ''
},
onLoad: function (options) {
// 页面创建时执行
},
onShow: function () {
// 页面出现在前台时执行
},
onReady: function () {
// 页面首次渲染完毕时执行
},
onHide: function () {
// 页面从前台变为后台时执行
},
onUnload: function () {
// 页面销毁时执行
},
// 输入框变化事件触发
bindKeyInput(e) {
this.setData({
nickName: e.detail.value
})
}
})
5.4 获取手机号
<button class="login-btn" open-type="getPhoneNumber" bindgetphonenumber="getPhoneNumber">获取手机号</button>
// 将code发送到服务器换取手机号进行绑定
wx.request({
url: "http://localhost:8087/auth/getPhoneNumber?pcode=" + res.code,
method: 'POST',
success: function (res) {
if (res.data.code === 200) {
const phoneNumber = res.data.data;
}
},
fail: function (e) {
console.log("注册失败")
}
});
@ApiOperation("获取手机号")
@GetMapping(value = "/getPhoneNumber")
public ResultVO<String> getPhoneNumber(@Valid @NotBlank(message = "微信pcode不能为空") String pcode) throws WxErrorException {
WxMaPhoneNumberInfo phoneNoInfo = wxMaService.getUserService().getPhoneNoInfo(pcode);
if (phoneNoInfo == null) {
return ResultVO.fail("获取手机号失败");
}
return ResultVO.success(phoneNoInfo.getPhoneNumber());
}