Step by Step之微信授权登录Java版
近期一个项目的微信小程序开发版本升级,原来的微信账号进行授权登录功能在微信升级后无法使用,看官方材料及百度发现没有一个相对完整实现,于是完成了基础功能后记录一下。
一、环境信息及实现基础思路
1、开发环境
项目开发环境信息:客户端-微信小程序;后端-Springboot;数据库-MySQL。
2、需求说明
小程序用户可无需登录,直接使用微信授权登录方式登录小程序,微信账号第一次授权登录会新增一个用户,用户名称和头像均为预先设定的默认值,登录成功后可自主修改用户名称和头像。
因较长时间里没有多端登录需求,所以可直接用微信小程序的openId,而无须记录unionId。
3、授权登录实现思路
微信账号授权登录分为两部分,第一部分是微信授权登录,具体分以下几步:一是微信小程序调用wx.login()获得临时登录凭证code,并发给后端;二是后端收到code后,与小程序的appId、appSecret一起,发送给微信接口服务器,并从微信接口服务返回session_key和openId;三是判断openId是否已经在用户表中已经存在,如存在表示该用户已经注册,直接返回加密后的token,如用户表中未找到该用户,则在用户表中插入该openId的新记录。
前后端及微信接口服务的交互时序图如下:
第二部分就是用户信息中昵称和用户头像信息的获取,项目上一个版本小程序使用getUserInfo获取用户信息,但微信升级后调整了授权流程,该函数已经无法获取用户的真实信息,只有一个默认头像和名称,目前只能通过用户信息修改页去引导用户主动填写头像和昵称。
二、Java后端实现
Step 1 数据库表调整
因上一个版本直接可获取用户昵称和头像信息,因此没有在用户表中存放openId,这次需要在用户表中新增一个字段。
alter table user_info add column open_id varchar(50) default null comment '微信openId';
Step 2 后端代码调整
Step 2.1 数据库字段调整相关
属于MVC开发过程中的常规调整,不再详述,一般需要在model中增加字段,在mapper.xml中增加基础字段等。
Step 2.2 新增微信授权登录API
在controller中增加新的映射/wxlogin,及对应的映射处理函数,核心逻辑为:
- 从POST请求中读取json格式的code;
- 通过application.yml配置文件读取微信服务器地址、appId、appSecret,并使用Hutool的HttpUtil去调用微信后端接口;
- 根据获取的openId查询是否为已有用户,如果是进行登录相关操作并返回token;如果为新用户,则添加新用户信息,进行登录相关操作并返回token。
部分参考代码为:
@PostMapping("/wxlogin")
@Operation(summary = "微信code预登录(用于小程序登录)" , description = "微信小程序登录")
public ServerResponseEntity<LoginToken> wxLogin(@RequestBody String szBody) {
JSONObject jsonBody = new JSONObject(szBody);
String szCode = jsonBody.getStr("code");
String appAuthUrl = authConfig.getAppAuthUrl().replace("{appid}", authConfig.getAppId()).replace("{appsecret}", authConfig.getAppSecret()).replace("{code}", szCode);
// get the open_id and session_key from wx official site
String szAuthResult = HttpUtil.get(appAuthUrl);
JSONObject jsonAuthResult = new JSONObject(szAuthResult);
String openId = jsonAuthResult.getStr("openid");
String sessionKey = jsonAuthResult.getStr("session_key");
// check the user using openid
if (userService.count(new LambdaQueryWrapper<User>().eq(User::getOpenId, openId)) == 0) {
// user is not registered, so register new user
User newUser = new User();
// code for setting fields of new user object
userService.save(newUser);
// code for user login
return ServerResponseEntity.success(token);
} else {
// user with same openId exists, so login and return
User user = getUser(openId);
// code for user login
return ServerResponseEntity.success(token);
}
}
Step 2.3 用户信息修改接口
因用户昵称、头像的手工获取相关实现主要在微信小程序端,因此后端没有新增API,而是利旧使用用户信息修改API-/user/setUserInfo。
三、微信小程序实现
Step 3.1 授权登录相关
wx.login({
success: res => {
// 发送code到后台换取token
var params = {
url: "/wxlogin",
method: "POST",
data: {
code: res.code
},
callBack: res => {
// login success, save the token to storage
wx.setStorageSync('token', res.accessToken);
this.toHomePage();
},
errCallBack: res => {
// not registered, move to register page
console.log(res.msg)
wx.showToast({
title: '授权登录失败,请注册用户',
icon: 'none'
});
this.toLoginPage();
}
};
http.request(params);
}
})
Step 3.2 昵称头像信息获取相关
通过一个用户头像及昵称修改页面实现,第一段代码为userinfo.wxml
<view class="userinfo">
<block>
<image class="userinfo-avatar" src="{{avatarUrl}}" mode="cover"></image>
</block>
<button class="avatar-wrapper" open-type="chooseAvatar" bind:chooseavatar="onChooseAvatar">
修改头像
</button>
<input type="nickname" value="{{nickName}}" class="nick-name-input" placeholder="请输入昵称" bindblur="changeNickName"/>
<button class="login-btn" bindtap="userinfoModify">用户信息更新</button>
</view>
第二段代码为userinfo.js
Page({
/**
* 页面的初始数据
*/
data: {
avatarUrl: '',
nickName: '',
},
onLoad() {
this.getUserProfile()
},
// 获取用户信息
getUserProfile() {
// get the avatar and nickname of user
var params = {
url: "/user/userInfo",
method: "GET",
data: {},
callBack: res => {
// set the local variables of avatar and nickname
this.setData({
nickName: res.nickName,
avatarUrl: res.pic == null ? 'https://mmbiz.qpic.cn/mmbiz/icTdbqWNOwNRna42FI242Lcia07jQodd2FJGIYQfG0LAJGFxM4FbnQP6yfMxBgJ0F3YRqJCJ1aPAK2dQagdusBZg/0': 'https://img.<company-name>.cn/' + res.pic
})
},
errCallBack: res => {
// error while getting the avatar and nickname
wx.showToast({
title: '获取用户信息错误',
})
}
};
http.request(params);
},
// 用户选择头像
onChooseAvatar(e) {
this.setData({
avatarUrl: e.detail.avatarUrl,
})
},
// 用户修改昵称
changeNickName(e) {
let name = e.detail.value;
if (name.length === 0) return;
this.setData({
nickName: e.detail.value
})
},
// 修改用户昵称、用户名等
userinfoModify() {
let uploadUrl = '';
var that = this;
// check the value of avatar and nickname
if (that.data.avatarUrl === '' || that.data.nickName === '') {
// value of field is empty
wx.showToast({
title: '用户信息不全',
});
return;
}
// upload the avatar to the server side
var avatar_params = {
url: "/file/upload",
filePath: that.data.avatarUrl,
method: "POST",
data: {},
callBack: function(res) {
// upload success
uploadUrl = res.data;
console.log('nickName:', that.data.nickName);
var params = {
url: "/user/setUserInfo",
method: "PUT",
data: {
avatarUrl: uploadUrl,
nickName: that.data.nickName
},
callBack: function(resUpdate) {
// update success
wx.showToast({
title: '用户信息更新成功',
})
},
errCallBack: function(resUpdate) {
// update failure
console.log(resUpdate);
wx.showToast({
title: '用户信息更新失败',
})
}
};
http.request(params);
},
errCallback: function(res) {
console.log(res);
wx.showToast({
title: '头像上传失败',
})
}
}
http.uploadFile(avatar_params);
},
请先 后发表评论~