微信小程序开发实战之微信授权登录
1.1 简介
接下来,这篇博文我们将一起学习下如何开发一个微信小程序的手机号授权登录功能。
在开始之前,我们先来搞明白 “ 为什么需要学习和掌握微信小程序登录功能的开发?”
- 微信小程序可以通过微信官方提供的登录能力方便地获取微信提供的用户身份标识,快速建立小程序内的用户体系。
- 微信手机号授权登录是微信小程序必不可少的环节。
1.2 理论篇-微信小程序登录流程时序图
在开始之前,我们先弄清楚微信小程序的登录流程:
上图中,我们了解到用户登录流程需要小程序
、开发者服务器
和微信接口服务
3个角色的参与。
- 小程序:用户使用的客户端,由于小程序运行在微信之上,因此小程序可以通过API获取微信用户的身份信息。
- 开发者服务器:小程序的后端服务器,用于为小程序用户提供服务。
- 微信接口服务:微信为开发者服务器提供的接口。
那么上图中的流程实际上就是:
1. 微信小程序在程序启动时通过调用微信服务的 wx.login()
方法获取 临时登录凭证code
- 在微信小程序中通过
wx.login()
获取登录凭证code
- 临时登录凭证
code
只能使用一次,有效期为5分钟,且被微信接口服务验证一次后就会失效。code
由小程序内部自动生成,每次调用wx.login()
获得的code
都不同。
2. 微信小程序将获取到的临时登录凭证code发送给开发者服务器
在获取code后,使用
wx.request()
将code发送给开发者服务器。
3. 开发者服务器通过微信接口服务校验登录凭证
- 开发者服务器调用
auth.code2Session
接口,需要将AppId
、AppSecret
、code
发送给微信接口服务校验登录凭证- 如果校验成功会返回
session_key
,openid
和UnionID
,- 其中请求参数:
AppId
是小程序的唯一标识AppSecret
是小程序的密钥code
是在微信小程序中通过wx.login()
获取到的临时登录凭证code
- 响应结果:
OpenID
:openid并不等同于微信用户id,同一个微信用户在不同AppId小程序中的openid是不同的。UnionID
:用户在微信开放平台帐号下的唯一标识(若当前小程序已绑定到微信开放平台帐号)session_key
:会话密钥session_key
是对用户数据进行 加密签名的密钥。- 开发者服务器不应该把
session_key
会话密钥下发到小程序,也不应该对外提供这个密钥。
4 然后开发者服务器返回自定义的登录成功标识 token 。
- 在用户登录成功后,开发者服务器将
openid
和session_key
保存,然后生成一个自定义登录态的token(令牌)响应给小程序,通过token可以查询到openid
和session_key
。- 小程序下次请求时只要携带
token
,就可以证明用户已经登录。
5. 一键获取用户头像和昵称的时代已结束
值得注意的是,早期可以在小程序启动时候获取用户的头像和昵称,后来被禁用了。
后来要求,点击按钮授权后才可以获取用户和昵称,如今也被禁用了。
现在,想要获取微信小程序昵称和头像的获取需要登录成功后,手动填写选择,变得更加麻烦了。。。
1.3 实践篇-编码实战
接下来我们需要一步一步实现如下目标:
- 小程序调用
wx.login()
方法,获取 code 然后传给开发者服务器- 开发者服务器调用
auth.code2Session
接口,需要将AppId
、AppSecret
、code
发送给微信接口服务校验登录凭证。- 然后将获取到的
session_key
,openid
和UnionID
保存到服务端,不要返回给客户端,自动创建账号返回 token 。- 然后每次微信小程序启动前都检查是否是登录状态,如果没有登录则调用登录接口进行登录。
那么 wx.login()
方法最佳实践应该写在哪里呢?
我们知道微信授权登录,应该在小程序启动的时候执行一次即可,因此最合适的地方是在 app.js中。
1.3.1 前端-微信小程序 编写调用登录相关代码
说下设计思路:
- 实现小程序启动时自动执行登录操作,因此我们放在 app.js中
- 在 app.js中会做如下事情:
- 检查全局变量中是否有这个 token,如果没有则从 Storage 文件缓存中获取
- 调用开发者服务器,检查 token 是否有效
- 如果 token有效则直接跳过
- 如果 token无效则调用登录方法,并将 token写入到 Storage 文件缓存中。
- index.js 中做如下事情:
- 点击登录按钮,触发事件监听:从 Storage中获取缓存中的 token
- 调用后端接口根据 token换取用户的登录信息,比如用户昵称等信息。
- PS:
- 值得注意的是,新版本微信小程序 APi微信昵称不再支持直接获取,
- 如果需要获取微信昵称,需要在用户中心下拉调用接口获取,因此反倒不如直接后端升成一个默认的昵称。
- 以后在用户中心让用户自己修改昵称,手机号,头像等信息即可。
13.1.1 实现小程序启动时自动执行登录操作
// 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 修改 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 修改 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 后端-开发者服务器提供登录相关接口
接口URI | 备注 |
---|---|
/wxMiniProgramAppService/wxMiniProgramLogin.do | 微信小程序调用开发者服务器登录接口 |
/wxMiniProgramAppService/checkWxMiniProgramLogin.do | 微信小程序调用开发者服务器检查是否登录且 token有效接口 |
/wxMiniProgramAppService/fetchWxUserInfo.do | 微信小程序调用开发者服务器获取用户信息接口 |
1.3.2.1微信小程序登录相关接口层
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 微信小程序登录相关 Service 接口层
/***
* @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 微信小程序登录相关 Service 实现类层
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 微信小程序服务接口类
/***
* @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 微信小程序服务接口实现类
/***
* @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 本节源码下载
- 微信小程序前端源码 javascript-base-wx-login-sample
- 微信小程序后端源码 wx-mini-program-proxy