若依Springboot整合微信小程序实现登录
前言
✅作者:TuNan
✨个人主页:图南的个人主页
😉欢迎关注🔎点赞😍收藏⭐留言💌
一、微信小程序官方登录流程图
主要的流程就是调用wx.getUserProfile向微信服务器发送请求获取code和其他的一些信息,然后再通过wx.login向我们的后台发送请求获取session_key和openid等其他信息
二、个人实现登录流程
1️⃣首先去我们的微信公众号平台拿到AppId和AppSecret。等一下我们会用到😉
2️⃣数据库设计
CREATE TABLE `user` (
`pk_id` bigint(20) NOT NULL COMMENT '主键',
`nickname` varchar(50) DEFAULT NULL COMMENT '用户昵称',
`service_man_id` bigint(20) DEFAULT NULL,
`avatarUrl` varchar(200) DEFAULT NULL COMMENT '用户头像',
`openid` varchar(50) DEFAULT NULL,
`unionid` varchar(50) DEFAULT NULL COMMENT '用户统一标识。针对一个微信开放平台帐号下的应用,同一用户的unionid是唯一的。',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP,
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`pk_id`) USING BTREE,
UNIQUE KEY `user_unionid_uindex` (`unionid`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;
这里service_man_id和unioinid可以不用
3️⃣Springboot后端
3.1 将我们刚才从微信公众号平台获取到的appld定义一个常量类
package com.ruoyi.gas.constant;
/**
* @Description 系统常量
* @Author TuNan
* @Date 2023/10/15
*/
public class SystemConstant {
/**
* 应用唯一标识,在微信开放平台提交应用审核通过后获得
*/
public static final String APPLETS_APPID="wx98935fbf9774bf5a";
/**
* 应用密钥AppSecret,在微信开放平台提交应用审核通过后获得
*/
public static final String APPLETS_SECRET ="adbecc45d036d4ba52aab8285c159216";
}
3.2实体类
package com.ruoyi.gas.domain;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
import java.io.Serializable;
/**
* 移动端用户对象 gas_xcx_user
*
* @author TuNan
* @date 2023-09-17
*/
@Getter
@Setter
@TableName("user")
@Accessors(chain = true)
public class User implements Serializable {
private static final long serialVersionUID = 1L;
@TableId("pk_id")
private Long pkId;
/**
* 用户昵称
*/
@TableField("nickname")
private String nickname;
/**
* 用户头像
*/
@TableField("avatarUrl")
private String avatarUrl;
/**
* 用户统一标识。针对一个微信开放平台帐号下的应用,同一用户的unionid是唯一的。
*/
@TableField("unionid")
private String unionId;
@TableField("openid")
private String openid;
private Long ServiceManId;
}
3.3 controller层
package com.ruoyi.gas.controller;
import com.ruoyi.gas.domain.dto.UserDTO;
import com.ruoyi.gas.domain.vo.ReWxUserInfoVO;
import com.ruoyi.gas.service.UserService;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.Map;
/**
* <p>
* 前端控制器
* </p>
*
* @author TuNan
* @since 2023/10/13
*/
@RestController
@RequestMapping("/gas/user")
public class UserController {
@Resource
private UserService userService;
/**
* 微信小程序登录
*
* @param requestData 请求参数
* @return ReWxUserInfoVO
*/
@PostMapping("/getUserLoginByApplets")
@SuppressWarnings("unchecked") // 用于抑制编译器产生警告信息。
public ReWxUserInfoVO getUserLoginByApplets(@RequestBody Map<String, Object> requestData) {
String code = (String) requestData.get("code");
Map<String, Object> userMap = (Map<String, Object>) requestData.get("user");
UserDTO userDTO = new UserDTO();
userDTO.setAvatarUrl((String) userMap.get("avatarUrl"));
userDTO.setCity((String) userMap.get("city"));
userDTO.setCountry((String) userMap.get("country"));
userDTO.setGender((Integer) userMap.get("gender"));
userDTO.setLanguage((String) userMap.get("language"));
userDTO.setNickName((String) userMap.get("nickName"));
System.out.println("userDTO = " + userDTO);
return userService.loginByWxApplets(code , userDTO);
}
}
3.4 服务层及实现
package com.ruoyi.gas.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.gas.domain.User;
import com.ruoyi.gas.domain.dto.UserDTO;
import com.ruoyi.gas.domain.vo.ReWxUserInfoVO;
/**
* <p>
* 服务类
* </p>
*
* @author TuNan
* @since 2023-09-18
*/
public interface UserService extends IService<User> {
/**
* 微信登录 小程序版
*/
ReWxUserInfoVO loginByWxApplets(String code, UserDTO userDTO);
}
package com.ruoyi.gas.service.impl;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.enums.DeviceType;
import com.ruoyi.common.helper.LoginHelper;
import com.ruoyi.gas.domain.User;
import com.ruoyi.gas.domain.dto.UserDTO;
import com.ruoyi.gas.domain.ro.AppletsWxLoginRO;
import com.ruoyi.gas.domain.vo.ReWxUserInfoVO;
import com.ruoyi.gas.mapper.UserMapper;
import com.ruoyi.gas.service.UserService;
import com.ruoyi.gas.utils.WxLoginUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
/**
* <p>
* 服务实现类
* </p>
*
* @author xiahaike
* @since 2023/10/13
*/
@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
@Override
public ReWxUserInfoVO loginByWxApplets(String code, UserDTO userDTO) {
// 1.通过code 获取到用户的微信信息
AppletsWxLoginRO getWxUserInfoRO = WxLoginUtil.toAppletsWxLogin(code);
// 2.数据库对比 unionid 是否已有信息 如果没有就保持数据库
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
// 3.通过查询pk_id 索引优化查询 无需回表查询用户信息 索引 openid
queryWrapper.eq("openid", getWxUserInfoRO.getOpenid());
User user = getOne(queryWrapper);
log.info("数据库查询用户是否已有账号==空则没有==={}====", user);
// 4.如果用户不存在则创建用户
if (ObjectUtil.isEmpty(user)) {
long userId = IdUtil.getSnowflakeNextId();
String nickName = userDTO.getNickName();
save(new User().setPkId(userId).setNickname(nickName).setOpenid(getWxUserInfoRO.getOpenid()).setAvatarUrl(userDTO.getAvatarUrl()).setServiceManId(0L));
LoginUser loginUser = BeanUtil.toBean(new User().setPkId(userId).setNickname(nickName), LoginUser.class);
loginUser.setUserType("sys_user");
loginUser.setUserId(IdUtil.getSnowflakeNextId());
LoginHelper.loginByDevice(loginUser, DeviceType.XCX);
return new ReWxUserInfoVO().setPkId(userId).setNickname(nickName)
.setSession_key(getWxUserInfoRO.getSession_key()).setOpenid(getWxUserInfoRO.getOpenid())
.setToken(StpUtil.getTokenValue()).setAvatarUrl(userDTO.getAvatarUrl()).setServiceManId(0L);
}
// 5.如果用户存在则直接登录
LoginUser loginUser = BeanUtil.toBean(user, LoginUser.class);
loginUser.setUserType("sys_user");
loginUser.setUserId(IdUtil.getSnowflakeNextId());
LoginHelper.loginByDevice(loginUser, DeviceType.XCX);
return new ReWxUserInfoVO().setPkId(user.getPkId()).setNickname(user.getNickname())
.setAvatarUrl(user.getAvatarUrl()).setSession_key(getWxUserInfoRO.getSession_key())
.setOpenid(getWxUserInfoRO.getOpenid()).setToken(StpUtil.getTokenValue())
.setServiceManId(user.getServiceManId());
}
}
3.5微信登录工具包
package com.ruoyi.gas.utils;
import cn.hutool.core.text.StrFormatter;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONUtil;
import com.ruoyi.gas.Enum.WxApiType;
import com.ruoyi.gas.constant.SystemConstant;
import com.ruoyi.gas.domain.ro.AppletsWxLoginRO;
import lombok.extern.slf4j.Slf4j;
/**
* @Description 微信登录工具包
* @Author TuNan
* @Date 2023/10/16
*/
@Slf4j
public class WxLoginUtil {
/**
*微信登录
* @param code 前端请求获取=>微信code
* @return void
* @author zhangjunrong
* @date 2022/12/19 20:16
*/
public static AppletsWxLoginRO toAppletsWxLogin(String code){
//1.通过前端给的code获取openid和access_token还有unionid
String getTokenOpenid = StrFormatter.format(WxApiType.APPLETS_GET_TOKEN_OPENID.getValue(), SystemConstant.APPLETS_APPID,SystemConstant.APPLETS_SECRET, code);
String data = HttpUtil.get(getTokenOpenid);
log.info("微信获取获取openid和unionid,返回结果======{}=====",data);
//json=>bean
//todo 获取用户信息失败 抛异常
return JSONUtil.toBean(data, AppletsWxLoginRO.class);
}
}
3.6 Ro及Vo
package com.ruoyi.gas.domain.ro;
import lombok.Data;
/**
* @Description 微信用户基础信息
* @Author TuNan
* @Date 2023/10/16
*/
@Data
public class WxUserInfoRO {
/**
* 用户昵称
*/
private String nickname;
/**
* 用户头像
*/
private String avatarUrl;
/**
* 用户微信统一标识
*/
private String unionid;
private String session_key;
private String openid;
}
package com.ruoyi.gas.domain.ro;
import lombok.Data;
/**
* @Description TODO
* @Author TuNan
* @Date 2023/09/18
*/
@Data
public class AppletsWxLoginRO {
/**
*普通用户标识,对该公众帐号唯一
*/
private String openid;
/**
*用户在开放平台的唯一标识符
*/
private String unionid;
private String session_key;
}
package com.ruoyi.gas.domain.vo;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* @Description 返回前端微信个人信息
* @Author TuNan
* @Date 2023/9/17 15:27
*/
@Data
@Accessors(chain = true)
public class ReWxUserInfoVO {
/**
* 用户id
*/
private Long pkId;
/**
* 用户昵称
*/
private String nickname;
/**
* 用户头像
*/
private String avatarUrl;
private String unionid;
private String session_key;
private String openid;
private String token;
private Long ServiceManId;
}
以下就是我们后端的所有代码,接下来是小程序端的代码
4️⃣小程序实现
4.1首先我们在app.js中定义全局变量
// app.js
App({
globalData: {
token: '',
openid: null,
sessionKey: null,
serviceManId: 0,
pkId:0, // 用于判断用户是否登录
}
})
4.2 然后是我们在主页调用wx.logind的js代码
const app = getApp()
// 点击登录按钮
login() {
if (this.data.pkId != 0) {
Toast.fail("您已登录,请勿重复提交")
} else {
console.log('执行了登录操作');
wx.getUserProfile({
desc: 'Wexin',
// 这里应该是向微信发送请求获取用户名和头像
success: (res) => {
this.setData({
userInfo: res.userInfo
})
let user = this.data.userInfo // user里面保存从微信获取的信息(用户名和头像)
// 向后台发送请求 将code、用户头像、用户名一起发送至后台
console.log(user)
wx.login({
success: (res) => {
console.log(res)
const code = res.code;
wx.request({
url: baseUrl + '/gas/user/getUserLoginByApplets',
method: "POST",
data: {
user,
code
},
success: (res) => {
// 后端返回的数据
var Mydata = res.data
// 将从服务器获取的token、session_key等保存到globalData中
app.globalData.token = Mydata.token
app.globalData.session_key = Mydata.session_key
app.globalData.serviceManId = Mydata.serviceManId
app.globalData.openid = Mydata.openid
app.globalData.pkId = Mydata.pkId
// 将serviceId和pkid保存到当前页面中
this.setData({
serviceManId: app.globalData.serviceManId,
pkId: res.data.pkId
});
}
})
},
})
}
})
}
},
到此我们所有流程就结束了🤞,出现以上效果说明就成功登录了😉
你好,我是博主
图南
,有问题可以留言评论或者私信我,大家一起交流学习!不过都看到这里啦,点个赞吧👩💻