初衷
公司要入小程序的坑,好久没写微信小程序,重新梳理一下登入逻辑。正好自己最近在看springBoot,所以也算一个前后端的小Demo,也算个人阶段性质的检验吧。
只是个人方案,不是标准。如果有问题,欢迎指正。
技术站
前端
微信原生
后端
springboot + mysql + MP + jwt
设计
表
前端缓存数据,都是假象。所以isAuth来判断是否用户授权。(即有用户信息)
需求分析
- 验证是否有token(此token只是jwt生成的,不是小程序平台的access_token)
- 存在
此时调用接口获取用户信息,看是否已授权用户信息 isAuth- 未授权
跳到授权页面。按钮授权,获取用户信息,存储用户信息和isAuth=true。再返回首页 - 已授权
放行,回到默认首页
- 未授权
- 不存在
- wx.login获取code,接入后台接口获取(openId),返回token 储存openId(此时无用户信息)。跳到授权页面,获取用户信息和isAuth
- 存在
前端代码
app.js
// app.js
App({
onLaunch() {
// 小程序生命周期,只有小程序加载完成时才运行一次,且从后台唤醒不触发。但凡接口获取的信息在这里不会及时反馈。所以我在onShow中
},
onShow() {
if (!wx.getStorageSync('token') ) {
// 登录
wx.login({
success: res => {
if (res.code) {
wx.request({
url: 'http://localhost:9527/wx/posden-spring-wx-user/getOpenId',
data: {
code: res.code
},
success: (res) => {
var openid = res.data.data.openid
this.globalData.openId = openid
// 将用户信息添加到数据库 (未授权时 数据库只有openId)接口isAuth是否用户已授权
wx.request({
url: 'http://localhost:9527/wx/posden-spring-wx-user/saveOrUpdateUser',
method: 'POST',
data: {
openId: openid
},
success: (res) => {
// token 本地存储
wx.setStorageSync('token', res.data.data.token)
if(!res.data.data.userInfo.isAuth){
wx.reLaunch({
url: '/pages/index/index',
})
}
}
})
},
fail: (error) => {
console.log(error)
}
})
}
}
})
}else {
this.queryUsreInfo()
}
},
//获取用户信息接口
queryUsreInfo: function () {
// 这里获取的用户信息是及时的(比如你换了头像,但数据库没更新),但还是存在一开始的问题,自己定义的后台接口权限问题,比如我后台不想获取用户信息,虽然你本地小程序权限已放开,但还是不行。你可以在你小程序加个刷新用户信息的功能。
// wx.getSetting({
// success: function (res) {
// if (res.authSetting['scope.userInfo']) {
// wx.getUserInfo({
// success: function (res) {
// //从数据库获取用户信息
// console.log(res)
// }
// });
// }
// }
// })
wx.request({
url: 'http://localhost:9527/wx/posden-spring-wx-user/getUserInfo',
header: {
'content-type': 'application/json',
'token': wx.getStorageSync('token')
},
success: (res)=> {
var userInfo = res.data.data.userInfo
console.log(userInfo.isAuth)
if(userInfo.isAuth){
wx.reLaunch({
url: '/pages/home/home',
})
}else{
wx.reLaunch({
url: '/pages/index/index',
})
}
}
})
},
globalData: {
userInfo: null,
openId: '',
}
})
授权页面
index.wxml
<!--index.wxml-->
<view wx:if="{{canIUse}}">
<view class='header'>
<image src='xxxxx.jpg'></image>
</view>
<view class='content'>
<view>申请获取以下权限</view>
<text>获得你的公开信息(昵称,头像等)</text>
</view>
<button class='bottom' type='primary' open-type="getUserInfo" lang="zh_CN" bindgetuserinfo="bindGetUserInfo">
授权登录
</button>
</view>
<view wx:else>请升级微信版本</view>
index.wcss
/**index.wxss**/
.header {
margin: 90rpx 0 90rpx 50rpx;
text-align: center;
width: 650rpx;
height: 300rpx;
line-height: 450rpx;
}
.header image {
width: 200rpx;
height: 200rpx;
border-radius: 50%
}
.content {
margin-left: 50rpx;
margin-bottom: 90rpx;
}
.content text {
display: block;
color: #9d9d9d;
margin-top: 40rpx;
}
.bottom {
border-radius: 80rpx;
margin: 70rpx 50rpx;
font-size: 35rpx;
}
index.js
const app = getApp();
Page({
data: {
//判断小程序的API,回调,参数,组件等是否在当前版本可用。
canIUse: wx.canIUse('button.open-type.getUserInfo'),
},
onLoad: function () {
var that = this;
// 查看是否授权
wx.getSetting({
success: function (res) {
if (res.authSetting['scope.userInfo']) {
wx.getUserInfo({
success: function (res) {
//从数据库获取用户信息
that.queryUsreInfo();
//用户已经授权过
wx.redirectTo({
url: '/pages/home/home'
})
}
});
}
}
})
},
bindGetUserInfo: function (e) {
if (e.detail.userInfo) {
//用户按了允许授权按钮
var that = this;
//插入登录的用户的相关信息到数据库
const {
userInfo,
encryptedData,
iv,
signature
} = e.detail
this.saveOrAdd(userInfo)
} else {
//用户按了拒绝按钮
wx.showModal({
title: '警告',
content: '您点击了拒绝授权,将无法进入小程序,请授权之后再进入!!!',
showCancel: false,
confirmText: '返回授权',
success: function (res) {
if (res.confirm) {
console.log('用户点击了“返回授权”')
}
}
})
}
},
// 更新数据库信息
saveOrAdd: function (userInfo) {
wx.request({
url: 'http://localhost:9527/wx/posden-spring-wx-user/saveOrUpdateUser',
method: 'POST',
data: {
openId: getApp().globalData.openId,
nickName: userInfo.nickName,
avatarUrl: userInfo.avatarUrl,
province: userInfo.province,
city: userInfo.city,
gender: userInfo.gender,
country: userInfo.country,
isAuth: true // 授权标识
},
header: {
'content-type': 'application/json'
},
success: (res)=> {
wx.setStorageSync('token', res.data.data.token)
//从数据库获取用户信息
wx.reLaunch({
url: '/pages/home/home'
})
}
});
},
//获取用户信息接口
queryUsreInfo: function () {
wx.request({
url: 'http://localhost:9527/wx/posden-spring-wx-user/getUserInfo',
header: {
'token': wx.getStorageSync('token')
},
success: (res)=> {
var userInfo = res.data.data.userInfo
getApp().globalData.userInfo = userInfo
}
})
},
})
前端展示效果
后端代码
有好的写法,或者有bug,欢迎交流。
package com.example.demo.wx.controller;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.example.demo.annotation.NeedLogin; // api需要token的的注解
import com.example.demo.utils.AppletsWeChatUtil;// 封装加密解密unionId的方法
import com.example.demo.utils.HttpUtil; // 封装http的工具类
import com.example.demo.utils.JwtUtil; // 封装jwt的工具类
import com.example.demo.utils.ReturnParam; // 封装的返回类
import com.example.demo.wx.entity.PosdenSpringWxUser;
import com.example.demo.wx.entity.UnicodeParamsDTO;
import com.example.demo.wx.server.PosdenSpringWxUserService;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;
/**
* <p>
* 前端控制器
* </p>
*
* @author posden
* @since 2021-01-20
*/
@RestController
@RequestMapping("/wx/posden-spring-wx-user")
public class PosdenSpringWxUserController {
@Resource
PosdenSpringWxUserService posdenSpringWxUserService;
private static final String appId = "wxa59f8c8d5c53c7ce";
private static final String appSecret = "60edd3c6a302d3a10bf89490b73bac28";
// 只获取openId
@GetMapping("/getOpenId")
public ReturnParam<Object> getUnicodeIdPassAuth(@RequestParam String code) {
String result = HttpUtil.get("https://api.weixin.qq.com/sns/jscode2session?appid="+appId+"&secret="+appSecret+"&js_code="+code+"&grant_type=authorization_code");
JSONObject res = JSONObject.parseObject(result);
ReturnParam<Object> returnParam = new ReturnParam<>();
returnParam.setSuccess(true);
returnParam.setData(res);
return returnParam;
}
// 通过解密方法获取openId和unionId 测试小程序好像没有unionId,但其他用户信息数据是有的
@PostMapping("/getUnicodeIdPassAuth")
public Map<String, String> getUnicodeIdPassAuth(@RequestBody UnicodeParamsDTO unicodeParamsDTO){
String encryptedData = unicodeParamsDTO.getEncryptedData();
String code = unicodeParamsDTO.getCode();
String iv = unicodeParamsDTO.getIv();
return AppletsWeChatUtil.oauth2GetUnionId(code,encryptedData,iv);
}
// 添加或更新
@PostMapping("/saveOrUpdateUser")
public ReturnParam<Object> saveOrUpdateUser(@RequestBody PosdenSpringWxUser posdenSpringWxUser){
String openId = posdenSpringWxUser.getOpenId();
QueryWrapper<PosdenSpringWxUser> updateWrapper = new QueryWrapper<>();
updateWrapper.eq("open_id", openId);
Boolean isAuth = posdenSpringWxUser.getIsAuth();
Map<String,String> payload = new HashMap<>();
payload.put("openId", openId);
String token = JwtUtil.getToken(payload);
ReturnParam<Object> returnParam = new ReturnParam<>();
int count = posdenSpringWxUserService.count(updateWrapper);
PosdenSpringWxUser item = posdenSpringWxUserService.getOne(updateWrapper);
if(count == 1) {
boolean save = posdenSpringWxUserService.update(posdenSpringWxUser, updateWrapper);
return getObjectReturnParam(isAuth, token, returnParam, item, save);
}else if(count == 0) {
boolean save = posdenSpringWxUserService.save(posdenSpringWxUser);
return getObjectReturnParam(isAuth, token, returnParam, item, save);
}else {
returnParam.setSuccess(false);
return returnParam;
}
}
private ReturnParam<Object> getObjectReturnParam(Boolean isAuth, String token, ReturnParam<Object> returnParam, PosdenSpringWxUser item, boolean save) {
Map<String, Object> data = new HashMap<>();
data.put("token", token);
data.put("isAuth", isAuth);
data.put("userInfo", item);
returnParam.setData(data);
returnParam.setSuccess(save);
return returnParam;
}
@NeedLogin()
@GetMapping("/getUserInfo")
public ReturnParam<Object> getUserInfo(HttpServletRequest headers) throws Exception {
String token = headers.getHeader("token");
String openId = JwtUtil.getOpenIdFromToken(token);
QueryWrapper<PosdenSpringWxUser> queryWrapper = new QueryWrapper<>();
System.out.println(openId);
queryWrapper.eq("open_id", openId);
PosdenSpringWxUser one = posdenSpringWxUserService.getOne(queryWrapper);
System.out.println(one);
ReturnParam<Object> returnParam = new ReturnParam<>();
returnParam.setSuccess(true);
Map<String, Object> data = new HashMap<>();
data.put("userInfo", one);
returnParam.setData(data);
return returnParam;
}
}
收工