Welcome Huihui's Code World ! !
接下来看看由辉辉所写的关于小程序的相关操作吧
目录
Welcome Huihui's Code World ! !
授权流程讲解
一.用户信息授权登录
1.wx.login
2.wx.getUserProfile
3.代码
WXML
JS
二.用户信息授权登录之后台交互
后端代码
WXML
JS
utils/user.js【封装的代码块】
三.手机号授权登录之后台交互
后端代码
WXML
JS
我们在使用微信中的小程序时,都会要我们进行授权,例如下面这样
那么这样的用户授权时怎么实现的呢,这一篇主要就是讲解用户授权登录的流程!!!
图片说明:
1.客户端调用 wx.login() 获取 临时登录凭证code,通过 wx.request() 发起网络请求,将 code 传给服务端
2、服务端使用 code + appid + appsecret 向微信换取 (调用 auth.code2Session 接口)用户唯一标识openid 和 会话密钥session_key
3、服务端自定义 登录状态token(与openid、session_key关联)返回客户端
4、客户端将 登录状态token 存入 缓存storage(推荐使用 wx.setStorageSync(‘key’, ‘value’) 同步存储)
5、客户端wx.request() 发起请求时,携带登录状态token (推荐使用 wx.getStorageSync(‘key’) 同步获取)
6、服务端通过 登录状态token 查询到对应 openid 和 session_key
7、验证成功后,返回业务数据给客户端
其中有两种方法,第一种方法是点击登录之后便直接获取了用户的个人信息,而第二种会询问用户是否同意授权,这样的话,会更具安全性
1.wx.login
这个方法主要用于获取用户的登录凭证(code)。在用户进入小程序时,前端会调用wx.login来获取这个code,然后将这个code发送给后台服务器。后台服务器再向微信发送请求,通过这个code来获取用户的唯一标识(openid)以及本次登录的会话密钥(session_key)。之后,后台服务器将这两个信息传回前台,用于自定义登录状态和用户唯一标识
2.wx.getUserProfile
这个方法主要用于获取用户的更多详细信息,如昵称、头像等。在使用这个方法之前,需要先调用wx.authorize接口来发起授权请求,请求用户授权提供这些信息。如果用户同意授权,就可以通过调用wx.getUserProfile方法来获取这些详细信息
3.代码
WXML
昵称:{{userInfo.nickName}} JS
// pages/index/index.js Page({ data: { userInfo: {}, canIUseGetUserProfile: false, }, onLoad() { // if (wx.getUserProfile) { // this.setData({ // canIUseGetUserProfile: true // }) // } }, getUserProfile(e) { console.log('getUserProfile') // 推荐使用 wx.getUserProfile 获取用户信息,开发者每次通过该接口获取用户个人信息均需用户确认 // 开发者妥善保管用户快速填写的头像昵称,避免重复弹窗 wx.getUserProfile({ desc: '用于完善会员资料', // 声明获取用户个人信息后的用途,后续会展示在弹窗中,请谨慎填写 success: (res) => { console.log(res); this.setData({ userInfo: res.userInfo, hasUserInfo: true }) } }) }, wxLogin: function(e) { debugger console.log('wxLogin') console.log(e.detail.userInfo); this.setData({ userInfo: e.detail.userInfo }) if (e.detail.userInfo == undefined) { app.globalData.hasLogin = false; util.showErrorToast('微信登录失败'); return; } }, /** * 生命周期函数--监听页面初次渲染完成 */ onReady() { }, /** * 生命周期函数--监听页面显示 */ onShow() { }, /** * 生命周期函数--监听页面隐藏 */ onHide() { }, /** * 生命周期函数--监听页面卸载 */ onUnload() { }, /** * 页面相关事件处理函数--监听用户下拉动作 */ onPullDownRefresh() { }, /** * 页面上拉触底事件的处理函数 */ onReachBottom() { }, /** * 用户点击右上角分享 */ onShareAppMessage() { } })
用户授权登录后,后台便会保存用户的信息
前期准备
①准备数据接口
②密钥以及appid
后端代码
package com.zking.ssm.wxcontroller; /** * @Autho donkee * @Since 2022/6/27 */ import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo; import com.alibaba.fastjson.JSONObject; import com.zking.ssm.annotation.LoginUser; import com.zking.ssm.model.UserInfo; import com.zking.ssm.model.WxLoginInfo; import com.zking.ssm.model.WxUser; import com.zking.ssm.service.UserToken; import com.zking.ssm.service.UserTokenManager; import com.zking.ssm.service.WxUserService; import com.zking.ssm.util.JacksonUtil; import com.zking.ssm.util.ResponseUtil; import com.zking.ssm.util.UserTypeEnum; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import cn.binarywang.wx.miniapp.api.WxMaService; import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult; import javax.servlet.http.HttpServletRequest; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; import java.util.HashMap; import java.util.Map; /** * 鉴权服务 */ @Slf4j @RestController @RequestMapping("/wx/auth") public class WxAuthController { @Autowired private WxMaService wxService; @Autowired private WxUserService userService; /** * 微信登录 * * @param wxLoginInfo * 请求内容,{ code: xxx, userInfo: xxx } * @param request * 请求对象 * @return 登录结果 */ @PostMapping("login_by_weixin") public Object loginByWeixin(@RequestBody WxLoginInfo wxLoginInfo, HttpServletRequest request) { //客户端需携带code与userInfo信息 String code = wxLoginInfo.getCode(); UserInfo userInfo = wxLoginInfo.getUserInfo(); if (code == null || userInfo == null) { return ResponseUtil.badArgument(); } //调用微信sdk获取openId及sessionKey String sessionKey = null; String openId = null; try { long beginTime = System.currentTimeMillis(); // WxMaJscode2SessionResult result = this.wxService.getUserService().getSessionInfo(code); // Thread.sleep(6000); long endTime = System.currentTimeMillis(); log.info("响应时间:{}",(endTime-beginTime)); sessionKey = result.getSessionKey();//session id openId = result.getOpenid();//用户唯一标识 OpenID } catch (Exception e) { e.printStackTrace(); } if (sessionKey == null || openId == null) { log.error("微信登录,调用官方接口失败:{}", code); return ResponseUtil.fail(); }else{ log.info("openId={},sessionKey={}",openId,sessionKey); } //根据openId查询wx_user表 //如果不存在,初始化wx_user,并保存到数据库中 //如果存在,更新最后登录时间 WxUser user = userService.queryByOid(openId); if (user == null) { user = new WxUser(); user.setUsername(openId); user.setPassword(openId); user.setWeixinOpenid(openId); user.setAvatar(userInfo.getAvatarUrl()); user.setNickname(userInfo.getNickName()); user.setGender(userInfo.getGender()); user.setUserLevel((byte) 0); user.setStatus((byte) 0); user.setLastLoginTime(new Date()); user.setLastLoginIp(IpUtil.client(request)); user.setShareUserId(1); userService.add(user); } else { user.setLastLoginTime(new Date()); user.setLastLoginIp(IpUtil.client(request)); if (userService.updateById(user) == 0) { log.error("修改失败:{}", user); return ResponseUtil.updatedDataFailed(); } } // token UserToken userToken = null; try { userToken = UserTokenManager.generateToken(user.getId()); } catch (Exception e) { log.error("微信登录失败,生成token失败:{}", user.getId()); e.printStackTrace(); return ResponseUtil.fail(); } userToken.setSessionKey(sessionKey); log.info("SessionKey={}",UserTokenManager.getSessionKey(user.getId())); Map
WXML
JS
// pages/auth/login/login.js var util = require('../../../utils/util.js'); var user = require('../../../utils/user.js'); const app = getApp(); Page({ /** * 页面的初始数据 */ data: { canIUseGetUserProfile: false, // 用于向前兼容 lock:false }, onLoad: function(options) { // 页面初始化 options为页面跳转所带来的参数 // 页面渲染完成 if (wx.getUserProfile) { this.setData({ canIUseGetUserProfile: true }) } //console.log('login.onLoad.canIUseGetUserProfile='+this.data.canIUseGetUserProfile) }, /** * 生命周期函数--监听页面初次渲染完成 */ onReady() { }, /** * 生命周期函数--监听页面显示 */ onShow() { }, getUserProfile(e) { console.log('getUserProfile'); // 推荐使用wx.getUserProfile获取用户信息,开发者每次通过该接口获取用户个人信息均需用户确认 // 开发者妥善保管用户快速填写的头像昵称,避免重复弹窗 wx.getUserProfile({ desc: '用于完善会员资料', // 声明获取用户个人信息后的用途,后续会展示在弹窗中,请谨慎填写 success: (res) => { //console.log(res); user.checkLogin().catch(() => { user.loginByWeixin(res.userInfo).then(res => { app.globalData.hasLogin = true; wx.navigateBack({ delta: 1 }) }).catch((err) => { app.globalData.hasLogin = false; if(err.errMsg=="request:fail timeout"){ util.showErrorToast('微信登录超时'); }else{ util.showErrorToast('微信登录失败'); } this.setData({ lock:false }) }); }); }, fail: (res) => { app.globalData.hasLogin = false; console.log(res); util.showErrorToast('微信登录失败'); } }); }, wxLogin: function(e) { console.log('wxLogin'); if (e.detail.userInfo == undefined) { app.globalData.hasLogin = false; util.showErrorToast('微信登录失败'); return; } user.checkLogin().catch(() => { user.loginByWeixin(e.detail.userInfo).then(res => { app.globalData.hasLogin = true; wx.navigateBack({ delta: 1 }) }).catch((err) => { app.globalData.hasLogin = false; if(err.errMsg=="request:fail timeout"){ util.showErrorToast('微信登录超时'); }else{ util.showErrorToast('微信登录失败'); } }); }); }, accountLogin() { console.log('开发中....') } })
utils/user.js【封装的代码块】
/** * 用户相关服务 */ const util = require('../utils/util.js'); const api = require('../config/api.js'); /** * Promise封装wx.checkSession */ function checkSession() { return new Promise(function(resolve, reject) { wx.checkSession({ success: function() { resolve(true); }, fail: function() { reject(false); } }) }); } /** * Promise封装wx.login */ function login() { return new Promise(function(resolve, reject) { wx.login({ success: function(res) { if (res.code) { resolve(res); } else { reject(res); } }, fail: function(err) { reject(err); } }); }); } /** * 调用微信登录 */ function loginByWeixin(userInfo) { return new Promise(function(resolve, reject) { return login().then((res) => { //登录远程服务器 util.request(api.AuthLoginByWeixin, { code: res.code, userInfo: userInfo }, 'POST').then(res => { if (res.errno === 0) { //存储用户信息 wx.setStorageSync('userInfo', res.data.userInfo); wx.setStorageSync('token', res.data.token); resolve(res); } else { reject(res); } }).catch((err) => { reject(err); }); }).catch((err) => { reject(err); }) }); } /** * 判断用户是否登录 */ function checkLogin() { return new Promise(function(resolve, reject) { if (wx.getStorageSync('userInfo') && wx.getStorageSync('token')) { checkSession().then(() => { resolve(true); }).catch(() => { reject(false); }); } else { reject(false); } }); } module.exports = { loginByWeixin, checkLogin, };
手机号授权登录的流程与用户信息授权登录流程是一样的,只不过向微信调用的接口有所不同
后端代码
package com.zking.ssm.wxcontroller; /** * @Autho donkee * @Since 2022/6/27 */ import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo; import com.alibaba.fastjson.JSONObject; import com.zking.ssm.annotation.LoginUser; import com.zking.ssm.model.UserInfo; import com.zking.ssm.model.WxLoginInfo; import com.zking.ssm.model.WxUser; import com.zking.ssm.service.UserToken; import com.zking.ssm.service.UserTokenManager; import com.zking.ssm.service.WxUserService; import com.zking.ssm.util.JacksonUtil; import com.zking.ssm.util.ResponseUtil; import com.zking.ssm.util.UserTypeEnum; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import cn.binarywang.wx.miniapp.api.WxMaService; import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult; import javax.servlet.http.HttpServletRequest; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; import java.util.HashMap; import java.util.Map; /** * 鉴权服务 */ @Slf4j @RestController @RequestMapping("/wx/auth") public class WxAuthController { /** * 绑定手机号码 * * @param userId * @param body * @return */ @PostMapping("bindPhone") public Object bindPhone(@LoginUser Integer userId, @RequestBody String body) { log.info("【请求开始】绑定手机号码,请求参数,body:{}", body); String sessionKey = UserTokenManager.getSessionKey(userId); String encryptedData = JacksonUtil.parseString(body, "encryptedData"); String iv = JacksonUtil.parseString(body, "iv"); WxMaPhoneNumberInfo phoneNumberInfo = null; try { phoneNumberInfo = this.wxService.getUserService().getPhoneNoInfo(sessionKey, encryptedData, iv); } catch (Exception e) { log.error("绑定手机号码失败,获取微信绑定的手机号码出错:{}", body); e.printStackTrace(); return ResponseUtil.fail(); } String phone = phoneNumberInfo.getPhoneNumber(); WxUser user = userService.selectByPrimaryKey(userId); user.setMobile(phone); if (userService.updateById(user) == 0) { log.error("绑定手机号码,更新用户信息出错,id:{}", user.getId()); return ResponseUtil.updatedDataFailed(); } Map
data = new HashMap (); data.put("phone", phone); log.info("【请求结束】绑定手机号码,响应结果:{}", JSONObject.toJSONString(data)); return ResponseUtil.ok(data); } } } WXML
JS
var util = require('../../../utils/util.js'); var api = require('../../../config/api.js'); var user = require('../../../utils/user.js'); var app = getApp(); Page({ /** * 页面的初始数据 */ data: { userInfo: {}, hasLogin: false, userSharedUrl: '' }, /** * 生命周期函数--监听页面加载 */ onLoad: function (options) { }, onShow: function () { let that = this; //获取用户的登录信息 let userInfo = wx.getStorageSync('userInfo'); this.setData({ userInfo: userInfo, hasLogin: true }); }, getPhoneNumber: function (e) { console.log(e); let that = this; if (e.detail.errMsg !== "getPhoneNumber:ok") { // 拒绝授权 return; } if (!this.data.hasLogin) { wx.showToast({ title: '绑定失败:请先登录', icon: 'none', duration: 2000 }); return; } util.request(api.AuthBindPhone, { iv: e.detail.iv, encryptedData: e.detail.encryptedData }, 'POST').then(function (res) { if (res.errno === 0) { let userInfo = wx.getStorageSync('userInfo'); userInfo.phone = res.data.phone;//设置手机号码 wx.setStorageSync('userInfo', userInfo); that.setData({ userInfo: userInfo, hasLogin: true }); wx.showToast({ title: '绑定手机号码成功', icon: 'success', duration: 2000 }); } }); }, exitLogin: function () { wx.showModal({ title: '', confirmColor: '#b4282d', content: '退出登录?', success: function (res) { if (!res.confirm) { return; } util.request(api.AuthLogout, {}, 'POST'); app.globalData.hasLogin = false; wx.removeStorageSync('token'); wx.removeStorageSync('userInfo'); wx.reLaunch({ url: '/pages/index/index' }); } }) } })
好啦,今天的分享就到这了,希望能够帮到你呢!