一、微信授权登录流程
小程序登录
小程序可以通过微信官方提供的登录能力方便地获取微信提供的用户身份标识,快速建立小程序内的用户体系。
步骤流程:
1.小程序调用wx.login() 获取 临时登录凭证code ,并回传到开发者服务器
2.开发者服务器以appid+appsecret+code换取 用户唯一标识openid和会话密钥session_key。
3.开发者服务器根据用户标识来生成自定义登录态用于后续业务逻辑中前后端交互时识别用户身份
4.客户端保存后端生成的自定义登录态,并在下一次发送请求的时候带上这个自定义登录态。
注意:临时登录凭证code只能使用一次
二、微信用户授权
下面我以两种方式的代码来给大家论证一下微信用户授权登录的流程,第一种不需要用户确认即可完成用户授权登录在开发中并不是那么的安全,第二种则是需要用户确认方可进行授权登录。
用户授权完成后端交互
我们使用用户授权后将用户信息保存到数据库方便下次用户发送请求的时候做身份认证。
封装的代码
utils/util.js
function formatTime(date) { var year = date.getFullYear() var month = date.getMonth() + 1 var day = date.getDate() var hour = date.getHours() var minute = date.getMinutes() var second = date.getSeconds() return [year, month, day].map(formatNumber).join('-') + ' ' + [hour, minute, second].map(formatNumber).join(':') } function formatNumber(n) { n = n.toString() return n[1] ? n : '0' + n } /** * 封封微信的的request */ function request(url, data = {}, method = "GET") { return new Promise(function (resolve, reject) { wx.request({ url: url, data: data, method: method, timeout:3000, header: { 'Content-Type': 'application/json', 'X-OA-Token': wx.getStorageSync('token') }, success: function (res) { if (res.statusCode == 200) { if (res.data.errno == 501) { // 清除登录相关内容 try { wx.removeStorageSync('userInfo'); wx.removeStorageSync('token'); } catch (e) { // Do something when catch error } // 切换到登录页面 wx.navigateTo({ url: '/pages/auth/login/login' }); } else { resolve(res.data); } } else { reject(res.errMsg); } }, fail: function (err) { reject(err) } }) }); } function redirect(url) { //判断页面是否需要登录 if (false) { wx.redirectTo({ url: '/pages/auth/login/login' }); return false; } else { wx.redirectTo({ url: url }); } } function showErrorToast(msg) { wx.showToast({ title: msg, image: '/static/images/icon_error.png' }) } function jhxLoadShow(message) { if (wx.showLoading) { // 基础库 1.1.0 微信6.5.6版本开始支持,低版本需做兼容处理 wx.showLoading({ title: message, mask: true }); } else { // 低版本采用Toast兼容处理并将时间设为20秒以免自动消失 wx.showToast({ title: message, icon: 'loading', mask: true, duration: 20000 }); } } function jhxLoadHide() { if (wx.hideLoading) { // 基础库 1.1.0 微信6.5.6版本开始支持,低版本需做兼容处理 wx.hideLoading(); } else { wx.hideToast(); } } module.exports = { formatTime, request, redirect, showErrorToast, jhxLoadShow, jhxLoadHide }
config/api.js
// 以下是业务服务器API地址 // 本机开发API地址 var WxApiRoot = 'http://localhost:8080/oapro/wx/'; // 测试环境部署api地址 // var WxApiRoot = 'http://192.168.191.1:8080/oapro/wx/'; // 线上平台api地址 //var WxApiRoot = 'https://www.oa-mini.com/demo/wx/'; module.exports = { IndexUrl: WxApiRoot + 'home/index', //首页数据接口 SwiperImgs: WxApiRoot+'swiperImgs', MettingInfos: WxApiRoot+'meeting/list', AuthLoginByWeixin: WxApiRoot + 'auth/login_by_weixin', //微信登录 UserIndex: WxApiRoot + 'user/index', //个人页面用户相关信息 AuthLogout: WxApiRoot + 'auth/logout', //账号登出 AuthBindPhone: WxApiRoot + 'auth/bindPhone' //绑定微信手机号 };
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('开发中....') } })
WXML代码
<view class="container"> <view class="login-box"> <button wx:if="{{canIUseGetUserProfile}}" type="primary" class="wx-login-btn" bindtap="getUserProfile">微信直接登录</button> <button wx:else open-type="getUserInfo" type="primary" class="wx-login-btn" bindgetuserinfo="wxLogin">微信直接登录</button> <button type="primary" class="account-login-btn" bindtap="accountLogin">账号登录</button> </view> </view>
后端配置appid+appsecret与数据库连接
后端代码
/** * 微信登录 * * @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<Object, Object> result = new HashMap<Object, Object>(); result.put("token", userToken.getToken()); result.put("tokenExpire", userToken.getExpireTime().toString()); userInfo.setUserId(user.getId()); if (!StringUtils.isEmpty(user.getMobile())) {// 手机号存在则设置 userInfo.setPhone(user.getMobile()); } try { DateFormat df = new SimpleDateFormat("yyyy-MM-dd"); String registerDate = df.format(user.getAddTime() != null ? user.getAddTime() : new Date()); userInfo.setRegisterDate(registerDate); userInfo.setStatus(user.getStatus()); userInfo.setUserLevel(user.getUserLevel());// 用户层级 userInfo.setUserLevelDesc(UserTypeEnum.getInstance(user.getUserLevel()).getDesc());// 用户层级描述 } catch (Exception e) { log.error("微信登录:设置用户指定信息出错:"+e.getMessage()); e.printStackTrace(); } result.put("userInfo", userInfo); log.info("【请求结束】微信登录,响应结果:{}", JSONObject.toJSONString(result)); return ResponseUtil.ok(result); }
效果演示