一个微信小程序登录实现
本文只是某项目的一个记录。
需求:

官方给出的流程如下:

我们先按这个图中流程实现。
第一步:获取open_id,首先得获取一个code,根据code进一步查询open_id。
wx.login({
success: res => {
//获取code
resolve(res.code)
}
})
我们把这个方法封装为getcode()
ps:wx.login(Object object) | 微信开放文档
第二步:在微信小程序首页的onload中,调用getWechatInfo方法,把code传到后台服务,进一步查询open_id:

async getWeChatInfo() {
//获取code
const code = await getCode()
let that=this;
//将code发给后端请求token
await uni.request({
url: loginRequest.baseURL + "getWeChat",
method: 'POST',
data: {
code
},
success: res => {
if (res.data.code === 200) {
this._openId = res.data.data.openId;
//自定义登录状态存入缓存
wx.setStorageSync('token', res.data.data.token);
//后面2个最好不要存在缓存中 xj
wx.setStorageSync('openId', res.data.data.openId);
wx.setStorageSync('loginFlag', res.data.data.loginFlag);
that.getUserInfoByOpenId();
//this.getUserInfoByOpenId();
} else {
this._openId = "-1";
uni.showToast({
title: res.data.msg,
icon: 'error',
duration: 2000
})
return;
}
}
})
},
第三步:服务端进行登录凭证校验接口appid+appsect+code:
我们通过集成第三方 WxMaService 在服务端进行登录凭证校验(参数:appid+appsect+code),从而获取session_key及openid。
服务端具体代码如下:
public JSONObject getWeChat(WeChatDTO weChatDTO) {
String code = weChatDTO.getCode();
log.info("code: {}", code);
if (StrUtil.isBlank(code)) {
throw new BizException("非法的授权码信息!");
}
WxMaProperties.Config config = wxMaProperties.getConfigs().stream().findFirst().get();
if (!wxMaService.switchover(config.getAppid())) {
throw new BizException(String.format("未找到对应appid=[%s]的配置,请核实!", config.getAppid()));
}
try {
WxMaJscode2SessionResult session = wxMaService.getUserService().getSessionInfo(code);
String sessionKey = session.getSessionKey();
String openid = session.getOpenid();
String unionid = session.getUnionid();
if (!StrUtil.isAllNotBlank(sessionKey, openid)) {
throw new BizException("登录失败!");
}
log.info("current sessionKey : {} ,openId:{}, unionid:{}", sessionKey, openid, unionid);
//new json object
JSONObject jsonObject = new JSONObject();
jsonObject.put("openId", openid);
jsonObject.put("unionId", unionid);
jsonObject.put("sessionKey", sessionKey);
jsonObject.put("code", code);
jsonObject.put("loginFlag", 1); // 1: online 0: offline
//产生一个随机数token
String token = StrUtil.uuid();
jsonObject.put("token", token);
//以token为key,将jsonObject存入redis
redisTemplate.opsForValue().set(token, jsonObject, 30, TimeUnit.MINUTES);
return jsonObject; // todo 返回值应该只有token 后面有时间再改
//return openid;
} catch (Exception exception) {
log.error("获取微信信息失败:{}", exception.getLocalizedMessage());
throw new BizException("获取微信信息失败!");
} finally {
WxMaConfigHolder.remove();//清理ThreadLocal
}
}
ps:WxMaService使用方式如下:
配置:
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-miniapp</artifactId>
<version>4.5.0</version>
</dependency>

它的使用方法及配置可以参见:
如何实现spring boot整合微信小程序订阅推送WxMaService的具体操作步骤
WxMaUserService (WxJava - MiniApp Java SDK 4.6.0 API)
我们把token 存放在小程序缓存当中。
业务请求的时候:
正常情况,每次微信小程序向业务端发起业务请求,都应该带上该token,后台服务根据该token,查询openId、session_key 等信息,做相关业务处理。
我们目前是用open_id作为token,这个办法值得商榷,下一版本再做修改!
另外一个问题是,某君希望我们把主页换掉,我们原理微信小程序主页是个列表页面,某君希望是改为业务主页面!

没钱?~~~~~~~~~~~~?
我们进入小程序主页的时候,就要获取用户信息,以此为依据控制展示内容。
获取用户信息(getUserInfoByOpenId):
首先说明一下,getUserInfoByOpenId ,这个方法就是根据openId,查询用户信息。
如果数据库没有这个openId的记录,说明这个用户不存在或者没有登录过,这个时候,页面还是主页面,但是,应该呈现登录按钮,点击登录按钮的时候,获取本机手机号,传递到后台,对比手机号记录,判断该用户是否注册用户。 当然 每次请求后台的时候,应该带上该openId,
如果后台数据库有这个openId记录,说明是合法且已经登录过的用户,这个时候,可以直接呈现主页面信息!同时呈现出直接进入按钮。否则这个按钮应该是登录按钮。
async getUserInfoByOpenId() {
uni.request({
url: loginRequest.baseURL +
"getUserInfoByOpenId",
method: 'POST',
data: {
openId: this._openId,
token: wx.getStorageSync('token')
},
success: res => {
console.log('------------------res-----------: ', res)
if (res.data.code === 200) {
res.data.data.doctors = res.data.data.doctors == null ? "" : res.data.data.doctors
//if res.data.data.phone is not null and not empty and doctors is not null and not empty ,then login success,to home page
if ( res.data.data.doctors != null && res.data.data.doctors != "") {
//将token保存本地
//wx.setStorageSync('token', res.data.data.openId)
// wx.setStorageSync('token', this._openId)
// wx.setStorageSync('loginFlag', 1)
//将用户信息保存本地
let userInfo = res.data.data.doctors
console.log('userInfo:', userInfo)
wx.setStorageSync('userInfo', userInfo)
//uni navigate to home page
let _status=res.data.data.doctors.loginStatus;
if(_status==1){ //后台已经有记录 直接登录
this.loginButtonType = 1;
}else{
this.loginButtonType = 0;
}
} else {
this.loginButtonType = 0;
}
} else {
// 需要获取手机号按钮
this.loginButtonType = 0;
return;
}
}
})
},
当loginButtonType=0的时候,按钮是多个获取手机号的按钮!
获取手机号:
系统获取手机号,手机号和后台存储的数据比对。
参考:uniapp开发微信小程序:用户手机号授权获取全流程详解与实战示例
先测试一下:
首先获取token:

用微信提供的功能获取code:
<template>
<view>
<!-- 其他界面元素 -->
<button open-type="getPhoneNumber" @getphonenumber="onGetPhoneNumber">授权并获取手机号</button>
</view>
</template>
getPhoneNumber (e) {
console.log(e.detail.code) // 动态令牌
console.log(e.detail.errMsg) // 回调信息(成功失败都会返回)
console.log(e.detail.errno) // 错误码(失败时返回)
}
getPhoneNumber 返回的 code 与 wx.login 返回的 code 作用是不一样的,不能混用。
有了ACCESS_TOKEN与code: 就可以调用wx api进一步测试:
POST https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=ACCESS_TOKEN


这一步返回就能获取手机号了。
获取到手机号后,我们要和后台记录比对。
先看一下其中的:
/**
* 处理医生信息
*
* @param phoneNumber 手机号
* @param userInfo 用户信息
* @param openId openId
* @param unionId unionId
* @return {@link Doctors}
*/
private Doctors handleDoctorInfo(String phoneNumber, WxMaUserInfo userInfo, String openId, String unionId) {
// 验证输入参数
if (StrUtil.isAllBlank(openId, phoneNumber)) {
throw new BizException("用户参数错误!");
}
// 构建查询条件
LambdaQueryWrapper<Doctors> queryWrapper = new QueryWrapper<Doctors>().lambda()
.eq(Doctors::getDelFlag, 1) // 未删除
.eq(Doctors::getAuditStatus, 1) // 已审核
.orderByAsc(Doctors::getCreateTime);
// 根据手机号或openid查询医生信息
String encodedPhone = StrUtil.isNotBlank(phoneNumber) ? PwdUtil.base64Encode(phoneNumber) : null;
if (encodedPhone != null) {
queryWrapper.eq(Doctors::getPhone, encodedPhone).or().eq(Doctors::getOpenId, openId);
} else {
queryWrapper.eq(Doctors::getOpenId, openId);
}
List<Doctors> doctorsList = iDoctorsService.list(queryWrapper);
if (CollUtil.isNotEmpty(doctorsList)) {
Doctors doctors = doctorsList.get(0);
// 更新医生信息
//doctors.setUserName(userInfo.getNickName());
//doctors.setPhone(StrUtil.isNotBlank(encodedPhone) ? phoneNumber : doctors.getPhone());
//doctors.setOpenId(openId);
// 从Redis获取头像URL或使用用户信息中的头像URL
if (StrUtil.isNotBlank(doctors.getOpenId())) {
Object imgUrl = redisTemplate.opsForValue().get(RedisUserKeyConstants.getUserImgUrlKey(doctors.getOpenId()));
doctors.setImgUrl(imgUrl != null ? imgUrl.toString() : userInfo.getAvatarUrl());
} else {
doctors.setImgUrl(userInfo.getAvatarUrl());
}
// 设置unionId(如果提供)
if (StrUtil.isNotBlank(unionId)) {
doctors.setUnionId(unionId);
}
// 更新数据库
doctors.setUpdateTime(new Date());
iDoctorsService.updateById(doctors);
// 缓存医生信息
if(StrUtil.isNotBlank(phoneNumber)){
redisTemplate.opsForValue().set(RedisUserKeyConstants.getPhoneKey(phoneNumber), doctors);
}
//if open`id is not null, then cache the openId and doctors
if(StrUtil.isNotBlank(openId)) {
redisTemplate.opsForValue().set(RedisUserKeyConstants.getOpenIdKey(doctors.getOpenId()), doctors);
}
// redisTemplate.opsForValue().set(
// StrUtil.isNotBlank(phoneNumber) ? RedisUserKeyConstants.getPhoneKey(phoneNumber) : RedisUserKeyConstants.getOpenIdKey(doctors.getOpenId()),
// doctors
// );
// 转换并返回医生信息(如果需要)
// doctors = doctorsConvertMapper.toExchangeDeptCodeOne(doctors); // 根据需要启用或注释掉
return doctors;
}
return null;
}
这段代码的作用,就是生成类似SELECT * FROM doctors WHERE (phone = ? OR open_id = ?) AND del_flag = 1 AND audit_status = 1 ORDER BY create_time ASC; 的语句,查询医生信息,然后刷新缓存。
能够查询到医生信息,用户就可以登录了.
现在我们已经实现用户第一次登录 又没有退出的啥情况 下次登录的时候 不用取得手机号
如果用户手机号是正确的,且通过了审核,再检查open_id,如果不存在 说明是第一次登录,那要保存open_id。
如果用户手机号是正确的,且通过了审核,再检查open_id,如果存在 说明该用户曾经登录过系统,值得信赖,可以直接登录。注意,手机号与绑定微信号是一致的,没问题,如果手机号与微信号不一致,则可能存在问题!(我们系统逻辑上控制了)
退出登录 :
用户第一次通过手机号登录后,为记录用户是否登录过, 同时 应该修改数据库用户表中的flag标志。当用户点击退出按钮的时候 要修改这个flag。
还有一个问题,原则上,我们不容许不用本机号码登录的情况。这个如何判断?还要查一下
费用:

顺便记个其他的东西,年级大,记性不好,常忘事~
ps:启动服务的命令:
#!/bin/bash
# 设置 Spring Boot 项目的 JAR 文件名
SPRING_BOOT_JAR="business-chat.jar"
# 检查该项目是否已经在运行中
RUNNING_PID=$(ps -ef | grep $SPRING_BOOT_JAR | grep -v grep | awk '{print $2}')
if [ -z "$RUNNING_PID" ]; then
# 如果项目未在运行中,则启动它
nohup java -jar $SPRING_BOOT_JAR > business-chat.log 2>&1 &
echo "Spring Boot 项目已启动"
else
echo "Spring Boot 项目已在运行中,进程ID为 $RUNNING_PID"
kill -9 $RUNNING_PID
echo "business-chat 项目已停止"
sleep 1
nohup java -jar $SPRING_BOOT_JAR > business-chat.log 2>&1 &
echo "restart success"
tail -f business-chat.log
fi
[root@VM-0-14-centos ~]# find / -name nginx.conf
/usr/local/nginx/nginx-1.24.0/conf/nginx.conf
/usr/local/nginx/conf/nginx.conf
[root@VM-0-14-centos ~]# cd /usr/locla
-bash: cd: /usr/locla: No such file or directory
[root@VM-0-14-centos ~]# cd /usr/local
[root@VM-0-14-centos local]# ls -l
total 56
drwxr-xr-x. 2 root root 4096 Nov 26 2023 bin
drwxr-xr-x. 2 root root 4096 Apr 11 2018 etc
drwxr-xr-x. 2 root root 4096 Apr 11 2018 games
drwxr-xr-x. 2 root root 4096 Apr 11 2018 include
drwxr-xr-x. 2 root root 4096 Apr 11 2018 lib
drwxr-xr-x. 2 root root 4096 Apr 11 2018 lib64
drwxr-xr-x. 2 root root 4096 Apr 11 2018 libexec
drwxr-xr-x 13 root root 4096 Nov 26 2023 nginx
drwxr-xr-x 17 root root 4096 Nov 24 2023 qcloud
drwxrwxr-x 7 root root 4096 Nov 16 15:26 redis-6.0.0
drwxr-xr-x. 2 root root 4096 Apr 11 2018 sbin
drwxr-xr-x. 5 root root 4096 Mar 7 2019 share
drwxr-xr-x. 2 root root 4096 Apr 11 2018 src
drwxr-xr-x 3 root root 4096 Nov 26 2023 yulang
[root@VM-0-14-centos local]# cd nginx
[root@VM-0-14-centos nginx]# ls -l
total 44
drwxr-xr-x 2 root root 4096 Nov 26 2023 cert
drwx------ 2 nobody root 4096 Oct 11 11:48 client_body_temp
drwxr-xr-x 2 root root 4096 May 2 2024 conf
drwx------ 2 nobody root 4096 Nov 26 2023 fastcgi_temp
drwxr-xr-x 9 root root 4096 Mar 15 2024 html
drwxr-xr-x 2 root root 4096 Oct 16 13:37 logs
drwxr-xr-x 9 1001 mysql 4096 Nov 26 2023 nginx-1.24.0
drwx------ 12 nobody root 4096 Nov 30 2023 proxy_temp
drwxr-xr-x 2 root root 4096 Nov 26 2023 sbin
drwx------ 2 nobody root 4096 Nov 26 2023 scgi_temp
drwx------ 2 nobody root 4096 Nov 26 2023 uwsgi_temp
[root@VM-0-14-centos nginx]# cd sbin
[root@VM-0-14-centos sbin]# ls -l
total 8240
-rwxr-xr-x 1 root root 8434768 Nov 26 2023 nginx
[root@VM-0-14-centos sbin]# ./nginx
[root@VM-0-14-centos sbin]#
更多推荐


所有评论(0)