2. Wechat applet development actual combat WeChat authorized login

1.1 Introduction

Next, in this blog post, we will learn together how to develop a mobile phone number authorization login function for a WeChat applet.

Before we start, let's figure out "Why do we need to learn and master the development of the WeChat applet login function?"

  • The WeChat mini-program can easily obtain the user identity provided by WeChat through the official login capability provided by WeChat, and quickly establish a user system within the mini-program.
  • Authorized login with WeChat mobile number is an essential part of WeChat Mini Programs.

1.2 Theory - Sequence Diagram of WeChat Mini Program Login Process

Before we start, let's figure out the login process of the WeChat applet:

insert image description here

小程序In the above figure, we learned that the user login process requires the participation 开发者服务器of 微信接口服务3 roles.

  • Mini Program : The client used by the user. Since the Mini Program runs on WeChat, the Mini Program can obtain the identity information of the WeChat user through the API.
  • Developer server : the backend server of the Mini Program, used to provide services for Mini Program users.
  • WeChat interface service : the interface provided by WeChat for the developer server.

Then the process in the above figure is actually:

1. The WeChat applet wx.login()obtains temporary login credentials by calling the WeChat service method when the program startscode

  • In the WeChat applet, wx.login()obtain the login credentials bycode
  • Temporary login credentials codecan only be used once and are valid for 5 minutes, and will become invalid after being verified once by the WeChat interface service.
  • codeIt is automatically generated inside the applet, and the result wx.login()obtained by each call codeis different.

2. The WeChat applet sends the obtained temporary login credential code to the developer server

After obtaining the code, use wx.request()to send the code to the developer server.

3. The developer server verifies the login credentials through the WeChat interface service

  • When the developer server calls auth.code2Sessionthe interface, it needs to send AppId, AppSecret, codeto the WeChat interface service to verify the login credentials
  • If the verification is successful, it will return session_key, openidand UnionID,
  • Among them request parameters:
    • AppIdIs the unique identifier of the applet
    • AppSecretis the key of the applet
    • codewx.login()It is the temporary login credentials obtained in the WeChat appletcode
  • Response result:
    • OpenID: The openid is not equal to the WeChat user id, and the same WeChat user has different openids in different AppId applets .
    • UnionID: The unique identifier of the user under the WeChat Open Platform account (if the current Mini Program has been bound to the WeChat Open Platform account)
    • session_key:sessionkey session_keyis the key used to cryptographically sign user data.
    • The developer server should not deliver session_keythe session key to the applet, nor should it provide this key externally.

4 Then the developer server returns a custom login success token.

  • After the user logs in successfully, the developer server will save openidthe and session_key, and then generate a custom login state token (token) response to the applet, and the openidand can be queried through the token session_key.
  • As long as the applet is carried in the next request token, it can prove that the user has logged in.

5. The era of one-click access to user avatars and nicknames is over

It is worth noting that in the early days, the user's avatar and nickname could be obtained when the applet was started, but it was later disabled.

Later, it was required that the user and nickname can only be obtained after clicking the button to authorize, and it is now disabled.

Now, if you want to obtain the nickname and avatar of the WeChat applet, you need to log in successfully, and manually fill in the selection, which becomes more troublesome. . .

1.3 Practice - coding practice

Next, we need to achieve the following goals step by step:

  1. The applet calls wx.login()the method, gets the code and sends it to the developer server
  2. When the developer server calls auth.code2Sessionthe interface, it needs to send AppId, AppSecret, codeto the WeChat interface service to verify the login credentials.
  3. Then save the obtained session_keyand to the server, do not return to the client, and automatically create an account and return the token openid.UnionID
  4. Then check whether it is logged in every time before the WeChat applet is started, and if not logged in, call the login interface to log in.

So wx.login()where should method best practices be written?

We know that WeChat authorization login should be executed once when the applet is started, so the most suitable place is in app.js.
insert image description here

1.3.1 Front-end - Wechat applet writes and calls login-related codes

Talk about the design idea:

  • Automatically execute the login operation when the applet starts, so we put it in app.js
    • In app.js will do the following:
      • Check if there is this token in the global variable, if not, get it from the Storage file cache
      • Call the developer server to check whether the token is valid
      • Skip directly if the token is valid
      • If the token is invalid, call the login method and write the token into the Storage file cache.
    • Do the following in index.js:
    • Click the login button to trigger event listening: get the token in the cache from Storage
    • Call the backend interface to exchange the user's login information based on the token, such as the user's nickname and other information.
  • PS:
  • It is worth noting that the new version of the WeChat applet APi WeChat nickname no longer supports direct acquisition.
  • If you need to obtain the WeChat nickname, you need to pull down the call interface in the user center to obtain it, so it is better to upgrade it to a default nickname directly on the backend.
  • In the future, users can modify their nickname, mobile phone number, avatar and other information in the user center.

13.1.1 Automatically execute the login operation when the applet is started

// app.js
App({
    
    
  onLaunch() {
    
    
    if(this.globalData.debugFlag){
    
    
      console.log('app.js===>onLaunch() do start!')
    }
    // 展示本地存储能力
    const logs = wx.getStorageSync('logs') || []
    logs.unshift(Date.now())
    wx.setStorageSync('logs', logs)

    // 实现小程序启动时自动执行登录操作
    this.checkWxLogin(res => {
    
    
      // 如果已登录且 token有效则无需操作
      if (res.haveLoginFlag) {
    
    
        if(this.globalData.debugFlag){
    
    
          console.log('app.js===>当前用户已登录不需要重新登录')
        }
      } else {
    
    
        // 如果没登录或登录无效则需要重新登录
        this.wxLogin()
      }
    })
    if(this.globalData.debugFlag){
    
    
      console.log('app.js===>onLaunch() do end!')
    }
  },
  // 检查微信用户是否已登录且 token 有效没有过期
  checkWxLogin(callback) {
    
    
    var token = this.globalData.token
    if (!token) {
    
    
      // 如果 token不存在
      token = wx.getStorageSync('token')
      if(token){
    
    
        // 如果缓存中存在 token 则更新
        this.globalData.token= token
      }else{
    
    
        // 返回登录失败
        // 函数返回值 可理解成 return false
        callback({
    
    
          haveLoginFlag: false
        })
      }
    } 
    // 检查登录 token 是否有效
    var checkWxMiniProgramLoginUrl = this.globalData.developerServerBaseUrl + '/wxMiniProgramAppService/checkWxMiniProgramLogin.do'
      if (this.globalData.debugFlag) {
    
    
         console.log('app.js===>开始请求验证token是否有效接口地址URL:' + checkWxMiniProgramLoginUrl)
      }
      // 如果 token 存在 则校验 token是否有效 
      wx.request({
    
    
        url: checkWxMiniProgramLoginUrl,
        method: 'POST',
        header: {
    
    
          'content-type': 'application/x-www-form-urlencoded' // 默认值
        },
        data: {
    
    
          token: token
        },
        success: res => {
    
    
          // 函数返回值 可理解成 return 接口返回的登录状态
          callback({
    
    
            haveLoginFlag: res.data.haveLoginFlag
          })
        }
      })
  },
  wxLogin() {
    
    
    wx.login({
    
    
      success: res => {
    
    
        // 发送 res.code 到后台换取 openId, sessionKey, unionId
        if (this.globalData.debugFlag) {
    
    
          console.log('app.js===>request wx login code:' + res.code);
        }
        var wxMiniProgramLoginUrl = this.globalData.developerServerBaseUrl + '/wxMiniProgramAppService/wxMiniProgramLogin.do';
        if (this.globalData.debugFlag) {
    
    
          console.log('app.js===>request login api url:' + wxMiniProgramLoginUrl);
        }
        wx.request({
    
    
          url: wxMiniProgramLoginUrl,
          method: 'POST',
          header: {
    
    
            'content-type': 'application/x-www-form-urlencoded'
          },
          data: {
    
    
            code: res.code
          },
          success: res => {
    
    
            var response = res.data
            if (response.code === 20000) {
    
    
              if(this.globalData.debugFlag){
    
    
                console.log('app.js===>微信登录成功');
                console.log('app.js===>打印当前用户登录token:' + response.data.token);
              }
              // 将 token 保存为公共数据 (用于多页面中访问)
              this.globalData.token = response.data.token
              if(this.globalData.debugFlag){
    
    
                console.log('app.js===>this.globalData.token=' + this.globalData.token)
              }
              // 将 token 保存到数据缓存 (下次打开小程序无需重新获取 token)
              wx.setStorage({
    
    
                key: 'token',
                data: this.globalData.token,
                success: res => {
    
    
                  if(this.globalData.debugFlag){
    
    
                    console.log('app.js===>用户登录token写入缓存成功')
                  }
                }// end success 
              })
            } else {
    
    
              if(this.globalData.debugFlag){
    
    
                console.log(response.message)
              }
            }
          }
        })
      }
    })
  },
  globalData: {
    
    
    // 调试打印信息
    debugFlag: true,
    // 开发者服务器基地址
    developerServerBaseUrl: 'https://www.your-server.com',
    // 将 token 保存为公共数据 (用于多页面中访问)
    token: null,
    // 是否已登录且 token有效
    haveLoginFlag: false,
    // 保存用户登录信息
    userInfo: null,
  }
})

13.1.2 Modify index.wxml

index.wxml

<!--index.wxml-->
<view class="container">
  <view class="userinfo">
    <block wx:if="{
     
     {canIUseOpenData}}">
      <view class="userinfo-avatar" bindtap="bindViewTap">
        <open-data type="userAvatarUrl"></open-data>
      </view>
      <!-- <open-data type="userNickName"></open-data> -->
      <text class="userinfo-nickname">登录用户昵称:{
   
   {userInfo.nickName}}</text>
    </block>
    <block wx:elif="{
     
     {!hasUserInfo}}">
      <button wx:if="{
     
     {canIUseGetUserProfile}}" bindtap="getUserProfile"> 获取头像昵称 </button>
      <button wx:elif="{
     
     {canIUse}}" open-type="getUserInfo" bindgetuserinfo="getUserInfo"> 获取头像昵称 </button>
      <view wx:else> 请使用1.4.4及以上版本基础库 </view>
    </block>
    <block wx:else>
      <image bindtap="bindViewTap" class="userinfo-avatar" src="{
     
     {userInfo.avatarUrl}}" mode="cover"></image>
      <text class="userinfo-nickname">{
   
   {userInfo.nickName}}</text>
    </block>
  </view>
 <view class="usermotto">
    <text class="user-motto">{
   
   {motto}}</text>
  </view>
  <!-- 自定义添加代码 -->
  <view >
    <button bindtap="fetchWxUserInfo">获取登录用户信息</button>
  </view>
  <view class="userinfo">
    <block wx:if="{
     
     {!hasUserInfo}}">
      <text class="userinfo-nickname">登录用户昵称:未登录</text>
    </block>
    <block wx:else>
      <text class="userinfo-nickname">登录用户昵称:{
   
   {userInfo.nickName}}</text>
    </block>
  </view>
</view>

13.1.3 Modify index.js

index.js

// index.js
// 获取应用实例
const app = getApp()

Page({
    
    
  data: {
    
    
    motto: 'Hello World',
    userInfo: {
    
    },
    hasUserInfo: false,
    canIUse: wx.canIUse('button.open-type.getUserInfo'),
    canIUseGetUserProfile: false,
    canIUseOpenData: wx.canIUse('open-data.type.userAvatarUrl') && wx.canIUse('open-data.type.userNickName') // 如需尝试获取用户信息可改为false
  },
  // 事件处理函数
  bindViewTap() {
    
    
    wx.navigateTo({
    
    
      url: '../logs/logs'
    })
  },
  onLoad() {
    
    
    console.log('index.js===>onLoad() method do start ')
    // <open-data>组件功能调整
    // 为优化用户体验,平台将于2022年2月21日24时起回收通过<open-data>展示个人信息的能力。如有使用该技术服务,
    // 请开发者及时对小程序进行调整,避免影响服务流程。
    // 查看详情:https://developers.weixin.qq.com/community/develop/doc/000e881c7046a8fa1f4d464105b001
    if (wx.getUserProfile) {
    
    
      this.setData({
    
    
        canIUseGetUserProfile: true
      })
    }
  },
  getUserProfile(e) {
    
    
    // 推荐使用wx.getUserProfile获取用户信息,开发者每次通过该接口获取用户个人信息均需用户确认,开发者妥善保管用户快速填写的头像昵称,避免重复弹窗
    wx.getUserProfile({
    
    
      desc: '展示用户信息', // 声明获取用户个人信息后的用途,后续会展示在弹窗中,请谨慎填写
      success: (res) => {
    
    
        console.log(res)
        this.setData({
    
    
          userInfo: res.userInfo,
          hasUserInfo: true
        })
      }
    })
  },
  getUserInfo(e) {
    
    
    // 不推荐使用getUserInfo获取用户信息,预计自2021年4月13日起,getUserInfo将不再弹出弹窗,并直接返回匿名的用户个人信息
    console.log(e)
    this.setData({
    
    
      userInfo: e.detail.userInfo,
      hasUserInfo: true
    })
  },
  fetchWxUserInfo() {
    
    
    var tokenParam = null
    wx.getStorage({
    
    
      key: "token",
      success: res => {
    
    
        if(app.globalData.debugFlag){
    
    
          console.log('index.js===>从缓存中取出来用户token成功,token:' + JSON.stringify(res.data))
        }
        tokenParam = res.data
        if (tokenParam) {
    
    
          var fetchWxUserInfoUrl = app.globalData.developerServerBaseUrl + '/wxMiniProgramAppService/fetchWxUserInfo.do'
          if(app.globalData.debugFlag){
    
    
            console.log('index.js===>根据 token获取登录用户信息请求参数:token=' + tokenParam)
            console.log('index.js===>根据 token获取登录用户信息请求URL=' + fetchWxUserInfoUrl)
          }
          // 根据 token 获取用户信息
          wx.request({
    
    
            url: fetchWxUserInfoUrl,
            method: 'POST',
            header: {
    
    
              'content-type': 'application/x-www-form-urlencoded' // 默认值
            },
            data: {
    
    
              token: tokenParam
            },
            success: res =>{
    
    
              var response = res.data
              if(app.globalData.debugFlag){
    
    
                console.log('index.js===>根据token获取登录用户信息返回结果:' + JSON.stringify(response))
              }
              if (response.code === 20000) {
    
    
                this.userInfo = response.data
                if(app.globalData.debugFlag){
    
    
                  console.log('index.js===>用户昵称:' + this.userInfo.nickName)
                  this.setData({
    
    
                    userInfo: response.data,
                    hasUserInfo: true
                  })
                }
              } else {
    
    
                if(app.globalData.debugFlag){
    
    
                  console.log('index.js===>根据token获取登录用户信息失败返回结果:' + response.message)
                }
              }
            }
          })
        }
      },
      fail(res) {
    
    
        if(app.globalData.debugFlag){
    
    
          console.log('index.js===>中取出来用户token失败:' + JSON.stringify(res.data))
        }
      }
    })

  }
})

1.3.2 Backend - The developer server provides login-related interfaces

Access URI Remark
/wxMiniProgramAppService/wxMiniProgramLogin.do WeChat applet calls developer server login interface
/wxMiniProgramAppService/checkWxMiniProgramLogin.do The WeChat applet calls the developer server to check whether it is logged in and the token is valid.
/wxMiniProgramAppService/fetchWxUserInfo.do WeChat applet calls developer server to obtain user information interface

1.3.2.1 WeChat applet login related interface layer

WxMiniProgramEndPoint.java

import org.springframework.web.bind.annotation.*;

/***
 * @author qingfeng.zhao
 * @date 2023/5/19
 * @apiNote 微信小程序相关接口
 */
@RequestMapping(value = "/wxMiniProgramAppService")
@RestController
public class WxMiniProgramEndPoint {
    
    

    final WxMiniProgramAppService wxMiniProgramAppService;

    public WxMiniProgramEndPoint(WxMiniProgramAppService wxMiniProgramAppService) {
    
    
        this.wxMiniProgramAppService = wxMiniProgramAppService;
    }

    /***
     * 检查微信用户登录状态
     * @param token 用户 token
     * @return 返回用户是否登录  true 有效登录  false 无效登录
     */
    @PostMapping(value = "/checkWxMiniProgramLogin.do")
    public VueElementAdminResponseVO checkWxMiniProgramLogin(String token){
    
    
        return wxMiniProgramAppService.checkWxMiniProgramLogin(token);
    }

    /**
     * 微信用户登录
     * @param code 微信用户登录请求 code
     * @return 返回登录用户 token
     */
    @PostMapping(value = "/wxMiniProgramLogin.do")
    public VueElementAdminResponseVO wxMiniProgramLogin(@RequestParam(value = "code")String code){
    
    
        return wxMiniProgramAppService.wxMiniProgramLogin(code);
    }

    /***
     * 根据登录用户 token 获取微信用户信息
     * @param token 微信用户登录成功的 token
     * @return 返回微信用户信息
     */
    @PostMapping(value = "/fetchWxUserInfo.do")
    public VueElementAdminResponseVO fetchWxUserInfo(String token){
    
    
        return wxMiniProgramAppService.fetchWxUserInfo(token);
    }
}

1.3.2.2 WeChat applet login related Service interface layer

/***
 * @author qingfeng.zhao
 * @date 2023/5/19
 * @apiNote 微信小程序 接口服务
 */
public interface WxMiniProgramAppService {
    
    
    /**
     * 微信小程序登陆接口
     * @param code 微信小程序通过 wx.login() 方法获取到的 code
     * @return 返回登录的 token
     */
    VueElementAdminResponseVO wxMiniProgramLogin(String code);
    /**
     * 微信小程序登录参数校验接口
     * @param code  微信小程序通过 wx.login() 方法获取到的 code
     * @return 返回参数校验结果
     */
    VueElementAdminResponseVO checkWxMiniProgramLoginParam(String code);
    /**
     * 检查是否是否登录 且 token有效
     * @param token 待检查的token
     * @return 返回是否已登录并且 token 有效
     */
    VueElementAdminResponseVO checkWxMiniProgramLogin(String token);
    /**
     * 根据 token 获取用户信息
     * @param token 登录用户 token
     * @return 微信用户信息
     */
    VueElementAdminResponseVO fetchWxUserInfo(String token);
    /**
     * 对象转化
     * @param wxUserBaseInfoEntity 持久化存储对象
     * @return 返回页面用户信息 VO对象
     */
    WxUserInfoVO convertToWxMiniProgramCustomerVO(WxUserBaseInfoEntity wxUserBaseInfoEntity);
}

1.3.2.3 WeChat applet login related Service implementation class layer

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestClientException;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

/***
 * @author qingfeng.zhao
 * @date 2023/5/19
 * @apiNote
 */
@Slf4j
@Service
public class WxMiniProgramAppServiceImpl implements WxMiniProgramAppService {
    
    

    final WxMiniProgramInterfaceProxyService wxMiniProgramInterfaceProxyService;
    final WxMiniProgramCustomerJpaRepository wxMiniProgramCustomerJpaRepository;

    public WxMiniProgramAppServiceImpl(WxMiniProgramInterfaceProxyService wxMiniProgramInterfaceProxyService, WxMiniProgramCustomerJpaRepository wxMiniProgramCustomerJpaRepository) {
    
    
        this.wxMiniProgramInterfaceProxyService = wxMiniProgramInterfaceProxyService;
        this.wxMiniProgramCustomerJpaRepository = wxMiniProgramCustomerJpaRepository;
    }

    /**
     *
     * @param code 微信小程序通过 wx.login() 方法获取到的 code
     * @return 返回用户登录成功的 token
     */
    @Override
    public VueElementAdminResponseVO wxMiniProgramLogin(String code) {
    
    
        log.info("微信小程序登录授权请求参数:{}",code);
        // 微信小程序授权登录参数校验
        VueElementAdminResponseVO vueElementAdminResponseVO=checkWxMiniProgramLoginParam(code);
        String trackId=vueElementAdminResponseVO.getTrackId();
        try {
    
    
            // 参数校验未通过则直接返回结果
            if(!vueElementAdminResponseVO.getCode().equals(ResponseCodeEnum.OK_SUCCESS.getCode())){
    
    
                return vueElementAdminResponseVO;
            }
            // 调用微信小程序登录功能
            WxMiniProgramLoginDTO wxMiniProgramLoginDTO=wxMiniProgramInterfaceProxyService.code2Session(code);
            log.info("开发者服务器-调用-微信接口服务-登录接口-返回结果:{}",wxMiniProgramLoginDTO);
            if(null==wxMiniProgramLoginDTO|| null==SmartStringUtils.trimToNull(wxMiniProgramLoginDTO.getOpenid())){
    
    
                vueElementAdminResponseVO.setCode(40001);
                vueElementAdminResponseVO.setMessage("微信小程序微信授权登陆失败");
                vueElementAdminResponseVO.setData(null);
                return vueElementAdminResponseVO;
            }
            // 微信小程序登录返回数据对象
            WxMiniProgramLoginVO wxMiniProgramLoginVO=new WxMiniProgramLoginVO();

            WxUserBaseInfoEntity savedWxUserBaseInfoEntity;
            Optional<WxUserBaseInfoEntity> wxMiniProgramCustomerEntityOptional=wxMiniProgramCustomerJpaRepository.findByOpenId(wxMiniProgramLoginDTO.getOpenid());
            if(wxMiniProgramCustomerEntityOptional.isPresent()){
    
    
                WxUserBaseInfoEntity wxUserBaseInfoEntity =wxMiniProgramCustomerEntityOptional.get();
                wxUserBaseInfoEntity.setUpdateTime(new Date());
                wxUserBaseInfoEntity.setToken(SmartStringUtils.generateSnowFakeIdStr());
                savedWxUserBaseInfoEntity =wxMiniProgramCustomerJpaRepository.save(wxUserBaseInfoEntity);
            }else{
    
    
                WxUserBaseInfoEntity wxUserBaseInfoEntity =new WxUserBaseInfoEntity();
                wxUserBaseInfoEntity.setUnionId(wxMiniProgramLoginDTO.getUnionid());
                wxUserBaseInfoEntity.setOpenId(wxMiniProgramLoginDTO.getOpenid());
                wxUserBaseInfoEntity.setNickName(SmartStringUtils.getCustomizedDigitsUuid(8));

                wxUserBaseInfoEntity.setSessionKey(wxMiniProgramLoginDTO.getSession_key());
                wxUserBaseInfoEntity.setToken(SmartStringUtils.generateSnowFakeIdStr());

                wxUserBaseInfoEntity.setStatus(true);
                wxUserBaseInfoEntity.setCreateTime(new Date());
                wxUserBaseInfoEntity.setUpdateTime(new Date());

                savedWxUserBaseInfoEntity =wxMiniProgramCustomerJpaRepository.save(wxUserBaseInfoEntity);
            }
            // 微信小程序授权登录 token
            wxMiniProgramLoginVO.setToken(savedWxUserBaseInfoEntity.getToken());
            vueElementAdminResponseVO.setCode(20000);
            vueElementAdminResponseVO.setMessage("微信小程序微信授权登陆成功");
            vueElementAdminResponseVO.setData(wxMiniProgramLoginVO);
        } catch (RestClientException e) {
    
    
            log.error("trackId:{},微信小程序微信授权登陆请求参数code:{},微信小程序微信授权登陆请求成功异常",trackId,code,e);
            vueElementAdminResponseVO.setCode(50000);
            vueElementAdminResponseVO.setMessage("微信小程序微信授权登陆请求成功异常");
            vueElementAdminResponseVO.setData(null);
        }
        return vueElementAdminResponseVO;
    }


    @Override
    public VueElementAdminResponseVO checkWxMiniProgramLoginParam(String code) {
    
    
        VueElementAdminResponseVO vueElementAdminResponseVO=new VueElementAdminResponseVO();
        try {
    
    
            if(null==SmartStringUtils.trimToNull(code)){
    
    
                vueElementAdminResponseVO.setCode(ResponseCodeEnum.REQUEST_PARAM_ERROR.getCode());
                vueElementAdminResponseVO.setMessage("登录时获取的code不可为空,可通过wx.login获取");
                vueElementAdminResponseVO.setData(null);
            }
            vueElementAdminResponseVO.setCode(ResponseCodeEnum.OK_SUCCESS.getCode());
            vueElementAdminResponseVO.setMessage("微信小程序登陆参数校验通过");
            vueElementAdminResponseVO.setData(null);
        } catch (Exception e) {
    
    
            log.error("trackId:{},微信小程序登陆参数校验异常,异常详情:",vueElementAdminResponseVO.getTrackId(),e);
            vueElementAdminResponseVO.setCode(ResponseCodeEnum.REQUEST_ERROR.getCode());
            vueElementAdminResponseVO.setMessage("微信小程序登陆参数校验异常");
            vueElementAdminResponseVO.setData(null);
        }
        return vueElementAdminResponseVO;
    }



    @Override
    public VueElementAdminResponseVO checkWxMiniProgramLogin(String token) {
    
    
        VueElementAdminResponseVO vueElementAdminResponseVO=new VueElementAdminResponseVO();
        try {
    
    
            Optional<WxUserBaseInfoEntity> wxMiniProgramCustomerEntityOptional=this.wxMiniProgramCustomerJpaRepository.findByToken(token);
            if(wxMiniProgramCustomerEntityOptional.isPresent()){
    
    
                Map<String,Object> resultMap=new HashMap<>();
                resultMap.put("haveLoginFlag",true);
                vueElementAdminResponseVO.setCode(20000);
                vueElementAdminResponseVO.setMessage("用户已登录");
                vueElementAdminResponseVO. setData(resultMap);
            }else{
    
    
                Map<String,Object> resultMap=new HashMap<>();
                resultMap.put("haveLoginFlag",false);
                vueElementAdminResponseVO.setCode(40001);
                vueElementAdminResponseVO.setMessage("token已失效,请重新登录");
                vueElementAdminResponseVO. setData(resultMap);
            }
        } catch (Exception e) {
    
    
            log.error("trackId:{},检查用户是否已登录异常,校验token:{},异常详情:",vueElementAdminResponseVO.getTrackId(),token,e);
            vueElementAdminResponseVO.setCode(50000);
            vueElementAdminResponseVO.setMessage("检查用户是否已登录异常");
            vueElementAdminResponseVO. setData(null);
        }
        return vueElementAdminResponseVO;
    }

    @Override
    public VueElementAdminResponseVO fetchWxUserInfo(String token) {
    
    
        VueElementAdminResponseVO vueElementAdminResponseVO=new VueElementAdminResponseVO();
        Optional<WxUserBaseInfoEntity> wxMiniProgramCustomerEntityOptional=this.wxMiniProgramCustomerJpaRepository.findByToken(token);
        if(wxMiniProgramCustomerEntityOptional.isPresent()){
    
    
            WxUserBaseInfoEntity wxUserBaseInfoEntity =wxMiniProgramCustomerEntityOptional.get();
            WxUserInfoVO wxMiniProgramLoginVO=convertToWxMiniProgramCustomerVO(wxUserBaseInfoEntity);
            vueElementAdminResponseVO.setCode(20000);
            vueElementAdminResponseVO.setMessage("获取用户信息成功");
            vueElementAdminResponseVO.setData(wxMiniProgramLoginVO);
        }else{
    
    
            vueElementAdminResponseVO.setCode(40001);
            vueElementAdminResponseVO.setMessage("无效的用户登录token");
            vueElementAdminResponseVO.setData(null);
        }
        return vueElementAdminResponseVO;
    }

    @Override
    public WxUserInfoVO convertToWxMiniProgramCustomerVO(WxUserBaseInfoEntity wxUserBaseInfoEntity) {
    
    
        WxUserInfoVO wxUserInfoVO =new WxUserInfoVO();
        BeanUtils.copyProperties(wxUserBaseInfoEntity, wxUserInfoVO);
        wxUserInfoVO.setId(String.valueOf(wxUserBaseInfoEntity.getId()));
        String createDateTimeStr = DateUtil.format(wxUserBaseInfoEntity.getCreateTime(), "yyyy-MM-dd HH:mm:ss");
        wxUserInfoVO.setCreateDateTime(createDateTimeStr);
        String updateDateTimeStr = DateUtil.format(wxUserBaseInfoEntity.getUpdateTime(), "yyyy-MM-dd HH:mm:ss");
        wxUserInfoVO.setUpdateDateTime(updateDateTimeStr);
        return wxUserInfoVO;
    }
}

1.3.2.4 WeChat applet service interface class

/***
 * @author qingfeng.zhao
 * @date 2023/5/19
 * @apiNote
 */
public interface WxMiniProgramInterfaceProxyService {
    
    
    /**
     * 小程序登陆-code2Session
     * @link https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/user-login/code2Session.html
     * 微信接口英文名:code2Session
     * @param code 登录时获取的 code,可通过wx.login获取
     * @return
     */
    WxMiniProgramLoginDTO code2Session(String code);
}

1.3.2.5 WeChat applet service interface implementation class

/***
 * @author qingfeng.zhao
 * @date 2023/5/19
 * @apiNote
 */
@Slf4j
@Service
public class WxMiniProgramInterfaceProxyServiceImpl implements WxMiniProgramInterfaceProxyService {
    
    

    final HttpHeaders httpHeaders;
    final WxMiniProgramProperties wxMiniProgramProperties;

    public WxMiniProgramInterfaceProxyServiceImpl(HttpHeaders httpHeaders, WxMiniProgramProperties wxMiniProgramProperties) {
    
    
        this.httpHeaders = httpHeaders;
        this.wxMiniProgramProperties = wxMiniProgramProperties;
    }

    /**
     * https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/user-login/code2Session.html
     * @param js_code 登录时获取的 code,可通过wx.login获取
     * @return
     */
    @Override
    public WxMiniProgramLoginDTO code2Session(String js_code) {
    
    
        String apiUrl = "https://api.weixin.qq.com/sns/jscode2session" + "?appid=" +
                wxMiniProgramProperties.getAppId() +
                "&secret=" +
                wxMiniProgramProperties.getAppSecret() +
                "&js_code=" +
                js_code +
                "&grant_type=" +
                "authorization_code";
        RestTemplate restTemplate = new RestTemplate(RestTemplateUtils.createSecureTransport());
        HttpEntity<String> entity = new HttpEntity<>(null,null);
        ResponseEntity<String> response = restTemplate.exchange(
                apiUrl,
                HttpMethod.GET,
                entity,
                String.class
        );
        log.info("微信小程序授权登录返回结果:{}",response.getBody());
        return SmartJackSonUtils.readValueToObject(response.getBody(), WxMiniProgramLoginDTO.class);
    }
}

1.4 Download the source code of this section

Guess you like

Origin blog.csdn.net/hadues/article/details/131078069