前言
主要是讲ruoyi前后端分离框架,springboot的微信小程序的实现方式,ruoyi的登录一般只针对账号密码登录,微信小程序登录却没有,实际上稍微改造一下就可以用。
一、微信小程序的登录接口
根据微信开放文档,微信有提供自己的登录接口,可用于现有的微信用户,并且授予基本信息
GET https://api.weixin.qq.***/sns/jscode2session
入参如下:
属性 | 类型 | 必填 | 说明 |
---|---|---|---|
appid | string | 是 | 小程序 appId |
secret | string | 是 | 小程序 appSecret |
js_code | string | 是 | 登录时获取的 code,可通过wx.login获取 |
grant_type | string | 是 | 授权类型,此处只需填写 authorization_code |
这个接口就是咱们要的登录接口,但是需要有js_code才行,这个js_code我们一般要前端返回
即可,至于appid和secret需要去微信公众平台那边获取,一般需要有开发者权限(在管理者的手上)
返回的类型我就不写那么详细了,咱们需要openid
也就是用户唯一标识,这个咱们需要存到数据库里面,以及用openid
和sessionKey
作为账号密码登录
二、微信用户数据库设计
代码如下:
CREATE TABLE `wechat_user` (
`userId` bigint NOT NULL AUTO_INCREMENT ***MENT '自增主键',
`openid` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL ***MENT '用户的唯一标识',
`unionid` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL ***MENT '公众号的唯一标识',
`nickname` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL ***MENT '昵称',
`name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL ***MENT '真实姓名',
`phonenumber` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL ***MENT '手机号码',
`gender` tinyint(1) DEFAULT '0' ***MENT '性别,0-未知 1-男性,2-女性',
`region` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL ***MENT '地区',
`avatar_url` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL ***MENT '用户头像URL',
`subscribe` tinyint NOT NULL DEFAULT '0' ***MENT '是否订阅公众号 0-否 1-是',
`del_flag` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '0' ***MENT '删除标志(0代表存在 2代表删除)',
`create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL ***MENT '创建者',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ***MENT '创建时间',
`update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL ***MENT '更新者',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ***MENT '更新时间',
`remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL ***MENT '备注',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE KEY `uk_openid` (`openid`) USING BTREE ***MENT '唯一索引,用于加速查找'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC ***MENT='微信用户表';
数据库设计尽量照着ruoyi的格式来,然后一些基本信息看自己需求来,我是偷懒了直接让gpt帮我生成了,最主要的是拿到openid,然后unionid一般也是根据需求来,咱们暂时用不上,不过也会预留字段。
三、springboot登录接口实现
1.新建实体WechatUser
在***mon
模块下新建一个WechatUser
,和SysUser
同级
java">package ***.ruoyi.***mon.core.domain.entity;
import org.apache.***mons.lang3.builder.ToStringBuilder;
import org.apache.***mons.lang3.builder.ToStringStyle;
import ***.ruoyi.***mon.core.domain.BaseEntity;
/**
* 微信用户实体
*
* @author Ricky
*/
public class WechatUser extends BaseEntity
{
private static final long serialVersionUID = 1L;
/** 自增主键 */
@TableId
private Long id;
/** 用户的唯一标识 */
@Excel(name = "用户的唯一标识")
private String openid;
/** 微信会话秘钥 */
@TableField(exist = false)
private String sessionKey;
/** 公众号的唯一标识 */
@Excel(name = "公众号的唯一标识")
private String unionid;
/** 昵称 */
@Excel(name = "昵称")
private String nickname;
/** 手机号码 */
@Excel(name = "手机号码")
private String phonenumber;
/** 性别,0-未知 1-男性,2-女性 */
@Excel(name = "性别,0-未知 1-男性,2-女性")
private Integer gender;
/** 地区 */
@Excel(name = "地区")
private String region;
/** 用户头像URL */
@Excel(name = "用户头像URL")
private String avatarUrl;
// 后面跟数据库设计一致
}
2.修改LoginUser类
在***.echain.***mon.core.domain.model.LoginUser的类加一个wechatUser
,生成getter
和setter
(用了lombok可不加),并且改写getPassword
和getUsername
方法
/**
* 微信用戶信息
*/
private WechatUser wechatUser;
public WechatUser getWechatUser() {
return wechatUser;
}
public void setWechatUser(WechatUser wechatUser) {
this.wechatUser = wechatUser;
}
@JsonIgnore
@Override
public String getPassword()
{
return user != null ? user.getPassword() : wechatUser.getSessionKey();
}
@Override
public String getUsername()
{
return user != null ? user.getUserName() : wechatUser.getOpenId();
}
3.增加wxLogin接口
这个接口是为了走SpringSecurity的登录验证方式,跟ruoyi原来的类似
public String wxLogin(String openId, String sessionKey, WechatUser user) {
LoginUser loginUser = new LoginUser(user);
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(openId, sessionKey);
authenticationToken.setDetails(loginUser);
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
// 生成token
return tokenService.createToken(loginUser);
}
这里是用了微信小程序登录接口返回的openId
和sessionKey
作为账号密码,由于sessionKey老是变动
,咱们就不需要存数据库,而openid是唯一的,需要存数据库
4.微信小程序登录接口
先添加一个调微信小程序提供的开放登录方法
/**
* 获取微信小程序的基本信息
* @param code
* @return
* @throws JsonProcessingException
*/
public static Map<String, Object> getWechatBaseInfo(String code) throws JsonProcessingException {
// 调用微信认证接口,获取 session_key 和 openid 等信息
String url = String.format(GET_AUTH_URL, APP_ID, WechatConfig.SECRET, code);
String response = restTemplate.getForObject(url, String.class);
return objectMapper.readValue(response, HashMap.class);
}
其中GET_AUTH_URL
、APP_ID
、SECRET
、code
是前面说的链接和入参
然后写springboot的登录接口(补充WeChatReq和WechatResp类)
@Data
public class WeChatReq {
private String code;
private String jscode;
private String encryptedData;
private String iv;
private String phonenumber;
private String sessionKey;
}
@Data
public class WechatResp {
private String code;
private String jscode;
private String openId;
private String unionId;
private String sessionKey;
private String a***essToken;
}
/**
* 小程序登录
*
* @param weChatReq 小程序wx.login返回的临时凭证
* @return
*/
@PostMapping("/mini/login")
@Transactional
public AjaxResult login(@RequestBody WeChatReq weChatReq) {
// 调用微信认证接口,获取 session_key 和 openid 等信息
Map<String, Object> resultMap;
try
{
String jscode = weChatReq.getJscode();
resultMap = getWechatBaseInfo(weChatReq.getJscode());
String sessionKey = (String) resultMap.get("session_key");
String openId = (String) resultMap.get("openid");
// 用户登录凭证(有效期五分钟)
if (StringUtils.isEmpty(jscode))
{
return AjaxResult.error("登录凭证不能为空");
}
// 查询是否已存在用户,如果不存在就把微信用户登录记录存储起来,这里我引入了mybatis-plus,如果没有的话可以直接写一个根据openid获取用户信息的方法.
QueryWrapper<WechatUser> wrapper = new QueryWrapper<>();
wrapper.eq("openid", openId);
WechatUser user = userServiceImpl.getOne(wrapper);
if(Objects.isNull(user)){
user = new WechatUser();
user.setOpenid(openId);
user.setCreateBy("mini");
user.setCreateTime(new Date());
user.setUpdateBy("mini");
user.setUpdateTime(new Date());
userServiceImpl.insertWechatUser(user);
}
}
WechatResp resp = new WechatResp();
resp.setJscode(weChatReq.getJscode());
resp.setCode(weChatReq.getCode());
resp.setSessionKey(sessionKey);
resp.setOpenId(openId);
JSONObject res = new JSONObject();
// 生成令牌
String token = loginService.wxLogin(openId, sessionKey, user);
res.put(Constants.TOKEN, token);
return AjaxResult.su***ess().put("data", res);
}
catch (Exception e)
{
String msg = "接口异常";
if (StringUtils.isNotEmpty(e.getMessage()))
{
msg = e.getMessage();
}
return AjaxResult.error(msg);
}
}
5.开放接口
***.echain.framework.config.SecurityConfig的configure
方法放行登录接口,在前面的/login、/captchaImage接口加一个/mini/login(根据自身需求加)
// 对于登录login 验证码captchaImage 允许匿名访问
.antMatchers("/login", "/captchaImage", "/mini/login").anonymous()
到这里就大功告成了,存数据库这个操作我就不详细说了,还有要注意如果没有引入mybatis-plus
,QueryWrapper
那边需要改成根据openid获取用户信息的方法
总结
实现之后,就能跟以前ruoyi的登录,通过token携带来访问接口,要注意的是这边的
权限控制
就失效了,需要自己改写权限的分配,因为以前是跟着sys_user
这个表的用户走的,这里不仔细讲了,一般微信小程序这边不需要做用户角色权限分配,可以自己加一层新的。
参考博客:RuoYi-Vue微信小程序登录授权