文章目录
- 1 前言
- 2.小程序代码
- 3.后端代码
- 3.1 yml配置文件中新增微信小程序id和秘钥
- 3.2 在数据库中新增open_id和union_id字段
- 3.3 在SysUser中新增两个字段
- 3.4 新增WxLoginBody接受小程序传来的数据
- 3.5 在ApplicationConfig类下注入RestTemplate
- 3.6 LoginUser中添加openId和unionId
- 3.7 SysUserMapper新增selectWxUserByOpenId
- 3.8 SysUserMapper.xml新增selectWxUserByOpenId
- 3.9 SysLoginController新增接口wxLogin
- 3.10 SysLoginService新增wxLogin方法
- 3.11 SecurityConfig中白名单加入"/wxLogin"
- 3.12 修改SysPermissionService下的getMenuPermission方法
- 下载源码
1 前言
通过若依框架实现微信小程序的授权登录。文章末尾提供源码下载
2.小程序代码
- 微信开发者工具基础库用的
2.26.2
- 修改
config.js
下baseUrl
baseUrl: 'http://localhost:8080',
app
模块配置微信登录
2.1 新增按钮微信授权登录
- 在登录按钮下,新增微信授权登录按钮
<button @click="wxHandleLogin" class="login-btn cu-btn block bg-green lg round">微信授权登录</button>
2.2 创建wx.Login和wxHandleLogin方法
- 调用
uni.getProvider
获取服务商信息 - 调用
uni.login
获取code
,并保存 - 调用
uni.getUserInfo
获取iv和encryptedData
,并保存 - 将
code
、iv
、encryptedData
发送到后端,让后端处理
// 微信登录方法
wxLogin(){
//获取服务商信息
uni.getProvider({
service: "oauth",
success: (res) => {
console.log(res);
if(~res.provider.indexOf("weixin")){
//登录
uni.login({
provider: "weixin",
success: (loginRes) => {
console.log("登录",loginRes);
this.wxLoginForm.code = loginRes.code;
}
})
}
}
})
},
//微信授权登录
async wxHandleLogin() {
console.log("微信小程序发起授权登录")
this.$modal.loading("登录中,请耐心等待...")
//获取用户信息
uni.getUserProfile({
desc:"获取用户信息",
success: (resInfo) => {
console.log("用户信息",resInfo)
//设置偏移量和加密数据
this.wxLoginForm.encryptedIv = resInfo.iv;
this.wxLoginForm.encryptedData = resInfo.encryptedData;
//向后端发起登录请求 TODO
this.sendWxLoginFormToLocalService();
},
fail: (res) => {
console.log("获取用户信息失败!",res)
}
})
},
create
钩子函数中调用
created() {
this.getCode()
this.wxLogin()
},
2.3 在@/api/login.js中新增wxLogin
- 参数分别是
code
、encryptedIv
、encryptedData
,需要后端提供接口,地址为/wxLogin
,请求方式为post
。
// 微信登录授权
export function wxLogin(code, encryptedIv, encryptedData) {
const data = {
code,
encryptedIv,
encryptedData
}
return request({
'url': '/wxLogin',
headers: {
isToken: false
},
'method': 'post',
'data': data
})
}
2.4 在@/store/modules/user.js下新增WxLogin
- 引入
api
下的wxLogin
import { login, wxLogin, logout, getInfo } from '@/api/login'
- 在
actions
中新增WxLogin
// 微信登录
WxLogin({ commit }, wxUserInfo) {
const code = wxUserInfo.code
const encryptedIv = wxUserInfo.encryptedIv
const encryptedData = wxUserInfo.encryptedData
return new Promise((resolve, reject) => {
wxLogin(code, encryptedIv, encryptedData).then(res => {
setToken(res.token)
commit('SET_TOKEN', res.token)
resolve()
}).catch(error => {
reject(error)
})
})
},
2.5 创建sendWxLoginFormToLocalService方法
- 全局使用
this.$store
,需在App.vue
中引入
import store from '@/store'
- 新增
sendWxLoginFormToLocalService
- 调用
action
中的WxLogin
//向本地服务发起请求
sendWxLoginFormToLocalService(){
console.log("向后端发起请求" + this.wxLoginForm);
this.$store.dispatch('WxLogin', this.wxLoginForm).then(() => {
this.$modal.closeLoading()
//登录成功后的逻辑 TODO
console.log("登录成功后的逻辑!")
this.loginSuccess()
}).catch(() => {
console.log("微信登录失败,请重新登录!")
this.$modal.msgError("微信登录失败,请重新登录!");
})
},
3.后端代码
3.1 yml配置文件中新增微信小程序id和秘钥
- 新增
appId
和appSecret
wx-app:
appId: wx32g4hdf54g51j56y
appSecret: 65fhsd6fh595sdh2s6dgf6jg6f2sdjsj
- 新增配置类
WxAppConfig
package com.ruoyi.common.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties(prefix = "wx-app")
public class WxAppConfig {
/** AppId */
private String appId;
/** AppSecret */
private String appSecret;
public String getAppId() {
return appId;
}
public void setAppId(String appId) {
this.appId = appId;
}
public String getAppSecret() {
return appSecret;
}
public void setAppSecret(String appSecret) {
this.appSecret = appSecret;
}
}
3.2 在数据库中新增open_id和union_id字段
3.3 在SysUser中新增两个字段
/** unionId */
private String unionId;
/** openId */
private String openId;
public String getUnionId() {
return unionId;
}
public void setUnionId(String unionId) {
this.unionId = unionId;
}
public String getOpenId() {
return openId;
}
public void setOpenId(String openId) {
this.openId = openId;
}
3.4 新增WxLoginBody接受小程序传来的数据
package com.ruoyi.common.core.domain.model;
public class WxLoginBody {
/** 临时登录凭证 code 只能使用一次 */
private String code;
/** 偏移量 */
private String encryptedIv;
/** 加密数据 */
private String encryptedData;
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getEncryptedIv() {
return encryptedIv;
}
public void setEncryptedIv(String encryptedIv) {
this.encryptedIv = encryptedIv;
}
public String getEncryptedData() {
return encryptedData;
}
public void setEncryptedData(String encryptedData) {
this.encryptedData = encryptedData;
}
@Override
public String toString() {
return "WxLoginBody{" +
"code='" + code + '\'' +
", encryptedIv='" + encryptedIv + '\'' +
", encryptedData='" + encryptedData + '\'' +
'}';
}
}
3.5 在ApplicationConfig类下注入RestTemplate
- 发送请求用
/**
* 远程调用
* @return
*/
@Bean
public RestTemplate restTemplate()
{
return new RestTemplate();
}
3.6 LoginUser中添加openId和unionId
/** unionId */
private String unionId;
/** openId */
private String openId;
3.7 SysUserMapper新增selectWxUserByOpenId
/**
* 根据openId查询用户信息
* @param openId
* @return
*/
public SysUser selectWxUserByOpenId(String openId);
3.8 SysUserMapper.xml新增selectWxUserByOpenId
<select id="selectWxUserByOpenId" parameterType="String" resultMap="SysUserResult">
<include refid="selectUserVo"/>
where u.open_id = #{openId} and u.del_flag = '0'
</select>
3.9 SysLoginController新增接口wxLogin
package com.ruoyi.web.controller.system;
@Autowired
private RestTemplate restTemplate;
@Autowired
private WxAppConfig wxAppConfig;
/**
* 登录验证
*
* @author ruoyi
*/
@RestController
public class SysLoginController
{
@PostMapping("/wxLogin")
public AjaxResult wxLogin(@RequestBody WxLoginBody wxLoginBody)
{
logger.info("登录参数:" + JSON.toJSONString(wxLoginBody));
//获取登录凭证 只能用一次
String code = wxLoginBody.getCode();
//秘钥
String encryptedIv = wxLoginBody.getEncryptedIv();
//加密数据
String encryptedData = wxLoginBody.getEncryptedData();
//想微信服务器发送请求获取用户信息
String url = "https://api.weixin.qq.com/sns/jscode2session?appid=" + wxAppConfig.getAppId() + "&secret=" + wxAppConfig.getAppSecret() + "&js_code=" + code + "&grant_type=authorization_code";
String res = restTemplate.getForObject(url, String.class);
JSONObject jsonObject = JSONObject.parseObject(res);
//获取session_key和openid
String sessionKey = jsonObject.getString("session_key");
String openid = jsonObject.getString("openid");
//解密
String decryptResult = "";
try {
//如果没有绑定微信开放平台,解析结果是没有unionid的。
decryptResult = decrypt(sessionKey,encryptedIv,encryptedData);
} catch (Exception e) {
e.printStackTrace();
return AjaxResult.error("微信登录失败!");
}
if (StringUtils.hasText(decryptResult)){
//如果解析成功,获取token
String token = loginService.wxLogin(decryptResult);
AjaxResult ajax = AjaxResult.success();
ajax.put(Constants.TOKEN, token);
return ajax;
}else{
return AjaxResult.error("微信登录失败!");
}
}
/**
* AES解密
* @param sessionKey
* @param encryptedIv
* @param encryptedData
*/
private String decrypt(String sessionKey,String encryptedIv,String encryptedData) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException {
//转化为字节数组
byte[] key = Base64.decode(sessionKey);
byte[] iv = Base64.decode(encryptedIv);
byte[] encData = Base64.decode(encryptedData);
// 如果密钥不足16位,那么就补足
int base = 16;
if (key.length % base != 0) {
int groups = key.length / base + (key.length % base != 0 ? 1 : 0);
byte[] temp = new byte[groups * base];
Arrays.fill(temp, (byte) 0);
System.arraycopy(key, 0, temp, 0, key.length);
key = temp;
}
// 如果初始向量不足16位,也补足
if (iv.length % base != 0) {
int groups = iv.length / base + (iv.length % base != 0 ? 1 : 0);
byte[] temp = new byte[groups * base];
Arrays.fill(temp, (byte) 0);
System.arraycopy(iv, 0, temp, 0, iv.length);
iv = temp;
}
AlgorithmParameterSpec ivSpec = new IvParameterSpec(iv);
String resultStr = null;
try {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
resultStr = new String(cipher.doFinal(encData), "UTF-8");
} catch (Exception e) {
logger.info("解析错误!");
e.printStackTrace();
}
//解析解密后的字符串
return resultStr;
}
}
3.10 SysLoginService新增wxLogin方法
/**
* 微信登录
*
* 备注:本来想用unionid作为唯一标识的,但是,小程序需要绑定微信开放平台,并且需要500大洋,我没钱,所以,就用openid作为唯一标识了。
* @param decryptResult 登录凭证 只能用一次
* @return
*/
public String wxLogin(String decryptResult){
//字符串转json
JSONObject jsonObject = JSONObject.parseObject(decryptResult);
//获取openid,这里本可以获取unionid的,可惜没钱
// String unionid = jsonObject.getString("unionid");
String openId = jsonObject.getString("openId");
//获取nickName
String nickName = jsonObject.getString("nickName");
//获取头像
String avatarUrl = jsonObject.getString("avatarUrl");
//还可以获取其他信息
//根据openid判断数据库中是否有该用户
//根据openid查询用户信息
SysUser wxUser = userMapper.selectWxUserByOpenId(openId);
//如果查不到,则新增,查到了,则更新
SysUser user = new SysUser();
if (wxUser == null) {
// 新增
// 设置姓名 ,默认使用昵称+随机数,防止重复姓名的发生 数据库中把username的长度修改的长一点
user.setUserName(IdUtils.fastSimpleUUID());
user.setNickName(nickName);
//数据库中把长度修改的长一点
user.setAvatar(avatarUrl);
// 如果有unionid,则设置unionid
// wxUser.setUnionId(unionid);
user.setOpenId(openId);
user.setCreateTime(DateUtils.getNowDate());
//新增 用户
userMapper.insertUser(user);
}else {
//更新
user = wxUser;
user.setNickName(nickName);
user.setAvatar(avatarUrl);
user.setUpdateTime(DateUtils.getNowDate());
userMapper.updateUser(user);
}
//组装token信息
LoginUser loginUser = new LoginUser();
loginUser.setOpenId(openId);
//如果有的话设置
// loginUser.setUnionId(unionid);
loginUser.setUser(user);
loginUser.setUserId(user.getUserId());
// 生成token
return tokenService.createToken(loginUser);
}
3.11 SecurityConfig中白名单加入"/wxLogin"
// 对于登录login 注册register 验证码captchaImage 允许匿名访问
.antMatchers("/login","/wxLogin", "/register", "/captchaImage").permitAll()
3.12 修改SysPermissionService下的getMenuPermission方法
这里的判断采用!CollectionUtils.isEmpty(roles)
,替换原有的!roles.isEmpty()
if (!CollectionUtils.isEmpty(roles) && roles.size() > 1)
{
// 多角色设置permissions属性,以便数据权限匹配权限
for (SysRole role : roles)
{
Set<String> rolePerms = menuService.selectMenuPermsByRoleId(role.getRoleId());
role.setPermissions(rolePerms);
perms.addAll(rolePerms);
}
}
下载源码
扫码关注公众号【立志成为好青年】回复 ruoyi 即可获取下载链接