• 微信小程序三种授权登录以及授权登录流程讲解


    🎉🎉欢迎来到我的CSDN主页!🎉🎉

    🏅我是Java方文山,一个在CSDN分享笔记的博主。📚📚

    🌟推荐给大家我的专栏《微信小程序开发实战》。🎯🎯

    👉点击这里,就可以查看我的主页啦!👇👇

    Java方文山的个人主页

    🎁如果感觉还不错的话请给我点赞吧!🎁🎁

    💖期待你的加入,一起学习,一起进步!💖💖

    请添加图片描述

    一、微信授权登录流程

    小程序登录

    小程序可以通过微信官方提供的登录能力方便地获取微信提供的用户身份标识,快速建立小程序内的用户体系。

    👇👇图解👇👇

    步骤流程:

    1.小程序调用wx.login() 获取 临时登录凭证code ,并回传到开发者服务器

    2.开发者服务器以appid+appsecret+code换取 用户唯一标识openid和会话密钥session_key

    3.开发者服务器根据用户标识来生成自定义登录态用于后续业务逻辑中前后端交互时识别用户身份

    4.客户端保存后端生成的自定义登录态,并在下一次发送请求的时候带上这个自定义登录态。

    注意:临时登录凭证code只能使用一次

    二、微信用户授权

    下面我以两种方式的代码来给大家论证一下微信用户授权登录的流程,第一种不需要用户确认即可完成用户授权登录在开发中并不是那么的安全,第二种则是需要用户确认方可进行授权登录。

    前期准备工作

    wxml代码展示

    1. <view>
    2. <button wx:if="{{canIUseGetUserProfile}}" type="primary" class="wx-login-btn" bindtap="getUserProfile">微信直接登录1button>
    3. <button wx:else open-type="getUserInfo" type="primary" class="wx-login-btn" bindgetuserinfo="wxLogin">微信直接登录2button>
    4. <image mode="scaleToFill" src="{{userInfo.avatarUrl}}" />
    5. <text>昵称:{{userInfo.nickName}}text>
    6. view>

    1.wx.login

    JS代码展示

    1. wxLogin: function(e) {
    2. debugger
    3. console.log('wxLogin')
    4. console.log(e.detail.userInfo);
    5. this.setData({
    6. userInfo: e.detail.userInfo
    7. })
    8. if (e.detail.userInfo == undefined) {
    9. app.globalData.hasLogin = false;
    10. util.showErrorToast('微信登录失败');
    11. return;
    12. }

    效果展示:

    看得出确实是走的这个方法并获取用户信息的,我们继续看一下另一个方法。

    2.wx.getUserProfile

    JS代码展示

    1. getUserProfile(e) {
    2. console.log('getUserProfile')
    3. // 推荐使用 wx.getUserProfile 获取用户信息,开发者每次通过该接口获取用户个人信息均需用户确认
    4. // 开发者妥善保管用户快速填写的头像昵称,避免重复弹窗
    5. wx.getUserProfile({
    6. desc: '用于完善会员资料', // 声明获取用户个人信息后的用途,后续会展示在弹窗中,请谨慎填写
    7. success: (res) => {
    8. console.log(res);
    9. this.setData({
    10. userInfo: res.userInfo,
    11. hasUserInfo: true
    12. })
    13. }
    14. })
    15. }

    效果展示: 

    3.用户授权完成后端交互 

    我们使用用户授权后将用户信息保存到数据库方便下次用户发送请求的时候做身份认证。

    封装的代码

    utils/util.js

    1. function formatTime(date) {
    2. var year = date.getFullYear()
    3. var month = date.getMonth() + 1
    4. var day = date.getDate()
    5. var hour = date.getHours()
    6. var minute = date.getMinutes()
    7. var second = date.getSeconds()
    8. return [year, month, day].map(formatNumber).join('-') + ' ' + [hour, minute, second].map(formatNumber).join(':')
    9. }
    10. function formatNumber(n) {
    11. n = n.toString()
    12. return n[1] ? n : '0' + n
    13. }
    14. /**
    15. * 封封微信的的request
    16. */
    17. function request(url, data = {}, method = "GET") {
    18. return new Promise(function (resolve, reject) {
    19. wx.request({
    20. url: url,
    21. data: data,
    22. method: method,
    23. timeout:3000,
    24. header: {
    25. 'Content-Type': 'application/json',
    26. 'X-OA-Token': wx.getStorageSync('token')
    27. },
    28. success: function (res) {
    29. if (res.statusCode == 200) {
    30. if (res.data.errno == 501) {
    31. // 清除登录相关内容
    32. try {
    33. wx.removeStorageSync('userInfo');
    34. wx.removeStorageSync('token');
    35. } catch (e) {
    36. // Do something when catch error
    37. }
    38. // 切换到登录页面
    39. wx.navigateTo({
    40. url: '/pages/auth/login/login'
    41. });
    42. } else {
    43. resolve(res.data);
    44. }
    45. } else {
    46. reject(res.errMsg);
    47. }
    48. },
    49. fail: function (err) {
    50. reject(err)
    51. }
    52. })
    53. });
    54. }
    55. function redirect(url) {
    56. //判断页面是否需要登录
    57. if (false) {
    58. wx.redirectTo({
    59. url: '/pages/auth/login/login'
    60. });
    61. return false;
    62. } else {
    63. wx.redirectTo({
    64. url: url
    65. });
    66. }
    67. }
    68. function showErrorToast(msg) {
    69. wx.showToast({
    70. title: msg,
    71. image: '/static/images/icon_error.png'
    72. })
    73. }
    74. function jhxLoadShow(message) {
    75. if (wx.showLoading) { // 基础库 1.1.0 微信6.5.6版本开始支持,低版本需做兼容处理
    76. wx.showLoading({
    77. title: message,
    78. mask: true
    79. });
    80. } else { // 低版本采用Toast兼容处理并将时间设为20秒以免自动消失
    81. wx.showToast({
    82. title: message,
    83. icon: 'loading',
    84. mask: true,
    85. duration: 20000
    86. });
    87. }
    88. }
    89. function jhxLoadHide() {
    90. if (wx.hideLoading) { // 基础库 1.1.0 微信6.5.6版本开始支持,低版本需做兼容处理
    91. wx.hideLoading();
    92. } else {
    93. wx.hideToast();
    94. }
    95. }
    96. module.exports = {
    97. formatTime,
    98. request,
    99. redirect,
    100. showErrorToast,
    101. jhxLoadShow,
    102. jhxLoadHide
    103. }

    config/api.js 

    1. // 以下是业务服务器API地址
    2. // 本机开发API地址
    3. var WxApiRoot = 'http://localhost:8080/oapro/wx/';
    4. // 测试环境部署api地址
    5. // var WxApiRoot = 'http://192.168.191.1:8080/oapro/wx/';
    6. // 线上平台api地址
    7. //var WxApiRoot = 'https://www.oa-mini.com/demo/wx/';
    8. module.exports = {
    9. IndexUrl: WxApiRoot + 'home/index', //首页数据接口
    10. SwiperImgs: WxApiRoot+'swiperImgs',
    11. MettingInfos: WxApiRoot+'meeting/list',
    12. AuthLoginByWeixin: WxApiRoot + 'auth/login_by_weixin', //微信登录
    13. UserIndex: WxApiRoot + 'user/index', //个人页面用户相关信息
    14. AuthLogout: WxApiRoot + 'auth/logout', //账号登出
    15. AuthBindPhone: WxApiRoot + 'auth/bindPhone' //绑定微信手机号
    16. };

    JS代码

    1. // pages/auth/login/login.js
    2. var util = require('../../../utils/util.js');
    3. var user = require('../../../utils/user.js');
    4. const app = getApp();
    5. Page({
    6. /**
    7. * 页面的初始数据
    8. */
    9. data: {
    10. canIUseGetUserProfile: false, // 用于向前兼容
    11. lock:false
    12. },
    13. onLoad: function(options) {
    14. // 页面初始化 options为页面跳转所带来的参数
    15. // 页面渲染完成
    16. if (wx.getUserProfile) {
    17. this.setData({
    18. canIUseGetUserProfile: true
    19. })
    20. }
    21. //console.log('login.onLoad.canIUseGetUserProfile='+this.data.canIUseGetUserProfile)
    22. },
    23. /**
    24. * 生命周期函数--监听页面初次渲染完成
    25. */
    26. onReady() {
    27. },
    28. /**
    29. * 生命周期函数--监听页面显示
    30. */
    31. onShow() {
    32. },
    33. getUserProfile(e) {
    34. console.log('getUserProfile');
    35. // 推荐使用wx.getUserProfile获取用户信息,开发者每次通过该接口获取用户个人信息均需用户确认
    36. // 开发者妥善保管用户快速填写的头像昵称,避免重复弹窗
    37. wx.getUserProfile({
    38. desc: '用于完善会员资料', // 声明获取用户个人信息后的用途,后续会展示在弹窗中,请谨慎填写
    39. success: (res) => {
    40. //console.log(res);
    41. user.checkLogin().catch(() => {
    42. user.loginByWeixin(res.userInfo).then(res => {
    43. app.globalData.hasLogin = true;
    44. wx.navigateBack({
    45. delta: 1
    46. })
    47. }).catch((err) => {
    48. app.globalData.hasLogin = false;
    49. if(err.errMsg=="request:fail timeout"){
    50. util.showErrorToast('微信登录超时');
    51. }else{
    52. util.showErrorToast('微信登录失败');
    53. }
    54. this.setData({
    55. lock:false
    56. })
    57. });
    58. });
    59. },
    60. fail: (res) => {
    61. app.globalData.hasLogin = false;
    62. console.log(res);
    63. util.showErrorToast('微信登录失败');
    64. }
    65. });
    66. },
    67. wxLogin: function(e) {
    68. console.log('wxLogin');
    69. if (e.detail.userInfo == undefined) {
    70. app.globalData.hasLogin = false;
    71. util.showErrorToast('微信登录失败');
    72. return;
    73. }
    74. user.checkLogin().catch(() => {
    75. user.loginByWeixin(e.detail.userInfo).then(res => {
    76. app.globalData.hasLogin = true;
    77. wx.navigateBack({
    78. delta: 1
    79. })
    80. }).catch((err) => {
    81. app.globalData.hasLogin = false;
    82. if(err.errMsg=="request:fail timeout"){
    83. util.showErrorToast('微信登录超时');
    84. }else{
    85. util.showErrorToast('微信登录失败');
    86. }
    87. });
    88. });
    89. },
    90. accountLogin() {
    91. console.log('开发中....')
    92. }
    93. })

    WXML代码

    1. <view class="container">
    2. <view class="login-box">
    3. <button wx:if="{{canIUseGetUserProfile}}" type="primary" class="wx-login-btn" bindtap="getUserProfile">微信直接登录button>
    4. <button wx:else open-type="getUserInfo" type="primary" class="wx-login-btn" bindgetuserinfo="wxLogin">微信直接登录button>
    5. <button type="primary" class="account-login-btn" bindtap="accountLogin">账号登录button>
    6. view>
    7. view>

     后端配置appid+appsecret与数据库连接

    后端代码

    1. /**
    2. * 微信登录
    3. *
    4. * @param wxLoginInfo
    5. * 请求内容,{ code: xxx, userInfo: xxx }
    6. * @param request
    7. * 请求对象
    8. * @return 登录结果
    9. */
    10. @PostMapping("login_by_weixin")
    11. public Object loginByWeixin(@RequestBody WxLoginInfo wxLoginInfo, HttpServletRequest request) {
    12. //客户端需携带code与userInfo信息
    13. String code = wxLoginInfo.getCode();
    14. UserInfo userInfo = wxLoginInfo.getUserInfo();
    15. if (code == null || userInfo == null) {
    16. return ResponseUtil.badArgument();
    17. }
    18. //调用微信sdk获取openId及sessionKey
    19. String sessionKey = null;
    20. String openId = null;
    21. try {
    22. long beginTime = System.currentTimeMillis();
    23. //
    24. WxMaJscode2SessionResult result = this.wxService.getUserService().getSessionInfo(code);
    25. // Thread.sleep(6000);
    26. long endTime = System.currentTimeMillis();
    27. log.info("响应时间:{}",(endTime-beginTime));
    28. sessionKey = result.getSessionKey();//session id
    29. openId = result.getOpenid();//用户唯一标识 OpenID
    30. } catch (Exception e) {
    31. e.printStackTrace();
    32. }
    33. if (sessionKey == null || openId == null) {
    34. log.error("微信登录,调用官方接口失败:{}", code);
    35. return ResponseUtil.fail();
    36. }else{
    37. log.info("openId={},sessionKey={}",openId,sessionKey);
    38. }
    39. //根据openId查询wx_user表
    40. //如果不存在,初始化wx_user,并保存到数据库中
    41. //如果存在,更新最后登录时间
    42. WxUser user = userService.queryByOid(openId);
    43. if (user == null) {
    44. user = new WxUser();
    45. user.setUsername(openId);
    46. user.setPassword(openId);
    47. user.setWeixinOpenid(openId);
    48. user.setAvatar(userInfo.getAvatarUrl());
    49. user.setNickname(userInfo.getNickName());
    50. user.setGender(userInfo.getGender());
    51. user.setUserLevel((byte) 0);
    52. user.setStatus((byte) 0);
    53. user.setLastLoginTime(new Date());
    54. user.setLastLoginIp(IpUtil.client(request));
    55. user.setShareUserId(1);
    56. userService.add(user);
    57. } else {
    58. user.setLastLoginTime(new Date());
    59. user.setLastLoginIp(IpUtil.client(request));
    60. if (userService.updateById(user) == 0) {
    61. log.error("修改失败:{}", user);
    62. return ResponseUtil.updatedDataFailed();
    63. }
    64. }
    65. // token
    66. UserToken userToken = null;
    67. try {
    68. userToken = UserTokenManager.generateToken(user.getId());
    69. } catch (Exception e) {
    70. log.error("微信登录失败,生成token失败:{}", user.getId());
    71. e.printStackTrace();
    72. return ResponseUtil.fail();
    73. }
    74. userToken.setSessionKey(sessionKey);
    75. log.info("SessionKey={}",UserTokenManager.getSessionKey(user.getId()));
    76. Map result = new HashMap();
    77. result.put("token", userToken.getToken());
    78. result.put("tokenExpire", userToken.getExpireTime().toString());
    79. userInfo.setUserId(user.getId());
    80. if (!StringUtils.isEmpty(user.getMobile())) {// 手机号存在则设置
    81. userInfo.setPhone(user.getMobile());
    82. }
    83. try {
    84. DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
    85. String registerDate = df.format(user.getAddTime() != null ? user.getAddTime() : new Date());
    86. userInfo.setRegisterDate(registerDate);
    87. userInfo.setStatus(user.getStatus());
    88. userInfo.setUserLevel(user.getUserLevel());// 用户层级
    89. userInfo.setUserLevelDesc(UserTypeEnum.getInstance(user.getUserLevel()).getDesc());// 用户层级描述
    90. } catch (Exception e) {
    91. log.error("微信登录:设置用户指定信息出错:"+e.getMessage());
    92. e.printStackTrace();
    93. }
    94. result.put("userInfo", userInfo);
    95. log.info("【请求结束】微信登录,响应结果:{}", JSONObject.toJSONString(result));
    96. return ResponseUtil.ok(result);
    97. }

     数据库展示

    效果演示

    三、微信手机号授权

    手机授权原理也是如此只不过调用的接口不同罢了。

    JS代码

    1. var util = require('../../../utils/util.js');
    2. var api = require('../../../config/api.js');
    3. var user = require('../../../utils/user.js');
    4. var app = getApp();
    5. Page({
    6. /**
    7. * 页面的初始数据
    8. */
    9. data: {
    10. userInfo: {},
    11. hasLogin: false,
    12. userSharedUrl: ''
    13. },
    14. /**
    15. * 生命周期函数--监听页面加载
    16. */
    17. onLoad: function (options) {
    18. },
    19. onShow: function () {
    20. let that = this;
    21. //获取用户的登录信息
    22. let userInfo = wx.getStorageSync('userInfo');
    23. this.setData({
    24. userInfo: userInfo,
    25. hasLogin: true
    26. });
    27. },
    28. getPhoneNumber: function (e) {
    29. console.log(e);
    30. let that = this;
    31. if (e.detail.errMsg !== "getPhoneNumber:ok") {
    32. // 拒绝授权
    33. return;
    34. }
    35. if (!this.data.hasLogin) {
    36. wx.showToast({
    37. title: '绑定失败:请先登录',
    38. icon: 'none',
    39. duration: 2000
    40. });
    41. return;
    42. }
    43. util.request(api.AuthBindPhone, {
    44. iv: e.detail.iv,
    45. encryptedData: e.detail.encryptedData
    46. }, 'POST').then(function (res) {
    47. if (res.errno === 0) {
    48. let userInfo = wx.getStorageSync('userInfo');
    49. userInfo.phone = res.data.phone;//设置手机号码
    50. wx.setStorageSync('userInfo', userInfo);
    51. that.setData({
    52. userInfo: userInfo,
    53. hasLogin: true
    54. });
    55. wx.showToast({
    56. title: '绑定手机号码成功',
    57. icon: 'success',
    58. duration: 2000
    59. });
    60. }
    61. });
    62. },
    63. exitLogin: function () {
    64. wx.showModal({
    65. title: '',
    66. confirmColor: '#b4282d',
    67. content: '退出登录?',
    68. success: function (res) {
    69. if (!res.confirm) {
    70. return;
    71. }
    72. util.request(api.AuthLogout, {}, 'POST');
    73. app.globalData.hasLogin = false;
    74. wx.removeStorageSync('token');
    75. wx.removeStorageSync('userInfo');
    76. wx.reLaunch({
    77. url: '/pages/index/index'
    78. });
    79. }
    80. })
    81. }
    82. })

    WXML代码

    1. <form bindsubmit="formSubmit">
    2. <view class='personal-data'>
    3. <view class='list'>
    4. <view class='item acea-row row-between-wrapper'>
    5. <view>头像view>
    6. <view class='pictrue'>
    7. <image src='{{userInfo.avatarUrl}}'>image>
    8. view>
    9. view>
    10. <view class='item acea-row row-between-wrapper'>
    11. <view>名字view>
    12. <view class='input'><input type='text' disabled='true' name='nickname' value='{{userInfo.nickName}}'>input>view>
    13. view>
    14. <view class='item acea-row row-between-wrapper'>
    15. <view>手机号码view>
    16. <button name='phone' class='phoneW' value='{{userInfo.phone}}' wx:if="{{!userInfo.phone}}" bindgetphonenumber="getPhoneNumber" hover-class='none' open-type='getPhoneNumber'>
    17. 点击获取
    18. button>
    19. <view class='input acea-row row-between-wrapper' wx:else>
    20. <input type='text' disabled='true' name='phone' value='{{userInfo.phone}}' class='id'>input>
    21. <text class='iconfont icon-suozi'>text>
    22. view>
    23. view>
    24. <view class='item acea-row row-between-wrapper'>
    25. <view>ID号view>
    26. <view class='input acea-row row-between-wrapper'>
    27. <input type='text' value='1000{{userInfo.userId}}' disabled='true' class='id'>input>
    28. <text class='iconfont icon-suozi'>text>
    29. view>
    30. view>
    31. view>
    32. <button class='modifyBnt' bindtap="exitLogin">退 出button>
    33. view>
    34. form>

    后端代码

    1. /**
    2. * 绑定手机号码
    3. *
    4. * @param userId
    5. * @param body
    6. * @return
    7. */
    8. @PostMapping("bindPhone")
    9. public Object bindPhone(@LoginUser Integer userId, @RequestBody String body) {
    10. log.info("【请求开始】绑定手机号码,请求参数,body:{}", body);
    11. String sessionKey = UserTokenManager.getSessionKey(userId);
    12. String encryptedData = JacksonUtil.parseString(body, "encryptedData");
    13. String iv = JacksonUtil.parseString(body, "iv");
    14. WxMaPhoneNumberInfo phoneNumberInfo = null;
    15. try {
    16. phoneNumberInfo = this.wxService.getUserService().getPhoneNoInfo(sessionKey, encryptedData, iv);
    17. } catch (Exception e) {
    18. log.error("绑定手机号码失败,获取微信绑定的手机号码出错:{}", body);
    19. e.printStackTrace();
    20. return ResponseUtil.fail();
    21. }
    22. String phone = phoneNumberInfo.getPhoneNumber();
    23. WxUser user = userService.selectByPrimaryKey(userId);
    24. user.setMobile(phone);
    25. if (userService.updateById(user) == 0) {
    26. log.error("绑定手机号码,更新用户信息出错,id:{}", user.getId());
    27. return ResponseUtil.updatedDataFailed();
    28. }
    29. Map data = new HashMap();
    30. data.put("phone", phone);
    31. log.info("【请求结束】绑定手机号码,响应结果:{}", JSONObject.toJSONString(data));
    32. return ResponseUtil.ok(data);
    33. }

    效果展示

    四、注销登录

    注销登录的前端代码同上,后端代码如下:

    后端代码

    1. /**
    2. * 注销登录
    3. */
    4. @PostMapping("logout")
    5. public Object logout(@LoginUser Integer userId) {
    6. log.info("【请求开始】注销登录,请求参数,userId:{}", userId);
    7. if (userId == null) {
    8. return ResponseUtil.unlogin();
    9. }
    10. try {
    11. UserTokenManager.removeToken(userId);
    12. } catch (Exception e) {
    13. log.error("注销登录出错:userId:{}", userId);
    14. e.printStackTrace();
    15. return ResponseUtil.fail();
    16. }
    17. log.info("【请求结束】注销登录成功!");
    18. return ResponseUtil.ok();
    19. }

    效果演示

     请添加图片描述

    到这里我的分享就结束了,欢迎到评论区探讨交流!!

    💖如果觉得有用的话还请点个赞吧 💖

  • 相关阅读:
    zkLogin构建者的最佳实践和业务思考
    草图大师SketchUp Pro 2023 for Mac
    分布式一致性算法:Raft
    Swift服务的基本使用
    Java-lambda表达式
    Spark 之 Projection
    六、《图解HTTP》一些关于Web的攻击手段
    P1347 排序(拓扑 + spfa判断环 or 拓扑[内判断环])
    8. 用Rust手把手编写一个wmproxy(代理,内网穿透等), HTTP改造篇之HPACK原理
    “罪魁祸首”已找到,微软回应修改 MIT 开源项目作者版权声明
  • 原文地址:https://blog.csdn.net/weixin_74318097/article/details/133963169