WeChat ミニプログラム開発 実践的な WeChat 認証ログイン
1.1 はじめに
次に、このブログ投稿では、WeChat アプレットの携帯電話番号認証ログイン機能を開発する方法を一緒に学びます。
始める前に、「なぜ WeChat アプレットのログイン機能の開発を学び、習得する必要があるのか?」を理解しましょう。
- WeChat ミニ プログラムは、WeChat が提供する公式ログイン機能を通じて WeChat が提供するユーザー ID を簡単に取得でき、ミニ プログラム内でユーザー システムを迅速に確立できます。
- WeChat 携帯電話番号による認証ログインは、WeChat ミニ プログラムの重要な部分です。
1.2 理論 - WeChat ミニ プログラムのログイン プロセスのシーケンス図
始める前に、WeChat アプレットのログイン プロセスを理解しましょう。
上の図では、ユーザーのログイン プロセスには3 つの役割の参加が必要である小程序
ことがわかりました。开发者服务器
微信接口服务
- ミニ プログラム: ユーザーが使用するクライアント ミニ プログラムは WeChat 上で実行されるため、API を通じて WeChat ユーザーの ID 情報を取得できます。
- 開発者サーバー: ミニ プログラムのバックエンド サーバー。ミニ プログラム ユーザーにサービスを提供するために使用されます。
- WeChat インターフェース サービス: WeChat によって開発者サーバーに提供されるインターフェース。
上図のプロセスは実際には次のようになります。
1. WeChat アプレットは、プログラムの開始時にwx.login()
WeChat サービス メソッドを呼び出して、一時的なログイン資格情報を取得します。code
- WeChat アプレットで、次のように
wx.login()
ログイン資格情報を取得します。code
- 一時的なログイン資格情報は
code
1 回のみ使用でき、有効期間は 5 分間で、WeChat インターフェイス サービスによって 1 回検証されると無効になります。code
アプレット内で自動生成され、呼び出しごとwx.login()
に得られる結果code
は異なります。
2. WeChat アプレットは、取得した一時ログイン資格情報コードを開発者サーバーに送信します。
コードを取得したら、 を使用して
wx.request()
コードを開発者サーバーに送信します。
3. 開発者サーバーは、WeChat インターフェイス サービスを通じてログイン資格情報を検証します。
- 開発者サーバーが
auth.code2Session
インターフェイスを呼び出すときは、ログイン資格情報を確認するために 、 、を WeChat インターフェイス サービスに送信する必要がAppId
ありAppSecret
ますcode
。- 検証が成功すると、と
session_key
、が返されます。openid
UnionID
- その中にはリクエストパラメータがあります:
AppId
アプレットの一意の識別子ですAppSecret
アプレットのキーですcode
これは、WeChat アプレットでwx.login()
取得された一時的なログイン資格情報です。code
- 応答結果:
OpenID
: openid は WeChat ユーザー ID と等しくなく、同じ WeChat ユーザーが異なる AppId アプレットに異なる openid を持っています。UnionID
: WeChat オープン プラットフォーム アカウントにあるユーザーの一意の識別子 (現在のミニ プログラムが WeChat オープン プラットフォーム アカウントにバインドされている場合)session_key
:sessionkey は、session_key
ユーザー データに暗号的に署名するために使用されるキーです。- 開発者サーバーは、
session_key
セッション キーをアプレットに配信したり、このキーを外部に提供したりしてはなりません。
4 次に、開発者サーバーはカスタム ログイン成功トークンを返します。
- ユーザーが正常にログインすると、開発者サーバーは
openid
と を保存しsession_key
、アプレットに対するカスタム ログイン状態トークン (トークン) 応答を生成します。また、 と はopenid
tokenを通じてクエリできますsession_key
。- アプレットが次のリクエストに含まれている限り
token
、ユーザーがログインしていることを証明できます。
5. ワンクリックでユーザーのアバターやニックネームにアクセスできる時代は終わった
初期の頃は、アプレットの起動時にユーザーのアバターとニックネームを取得できましたが、後に無効になったことは注目に値します。
その後、承認ボタンをクリックした後にのみユーザーとニックネームを取得できるようになり、現在は無効になっています。
さて、WeChat アプレットのニックネームとアバターを取得したい場合は、ログインに成功し、手動で選択項目を入力する必要があり、さらに面倒になります。。。
1.3 練習 - コーディングの練習
次に、次の目標を段階的に達成する必要があります。
- アプレットは
wx.login()
メソッドを呼び出し、コードを取得して開発者サーバーに送信します。- 開発者サーバーが
auth.code2Session
インターフェイスを呼び出すときは、ログイン資格情報を確認するために 、 、を WeChat インターフェイス サービスに送信する必要がAppId
ありAppSecret
ますcode
。- 次に、取得した
session_key
と をサーバーに保存し、クライアントには戻さず、自動的にアカウントを作成してトークンを返しますopenid
。UnionID
- その後、WeChat アプレットを起動する前に毎回ログインしているかどうかを確認し、ログインしていない場合はログイン インターフェイスを呼び出してログインします。
それでは、wx.login()
メソッドのベストプラクティスはどこに記述すべきでしょうか?
WeChat 認証ログインはアプレットの起動時に 1 回実行する必要があることがわかっているため、最も適切な場所は app.js 内です。
1.3.1 フロントエンド - Wechat アプレットはログイン関連のコードを作成および呼び出します
デザインのアイデアについて話します。
- アプレット起動時にログイン操作を自動的に実行するのでapp.jsに入れておきます。
- app.js では次のことを行います。
- グローバル変数にこのトークンがあるかどうかを確認し、ない場合はストレージ ファイル キャッシュから取得します。
- 開発者サーバーを呼び出してトークンが有効かどうかを確認します
- トークンが有効な場合は直接スキップします
- トークンが無効な場合は、ログイン メソッドを呼び出し、トークンをストレージ ファイル キャッシュに書き込みます。
- Index.js で次の操作を実行します。
- ログイン ボタンをクリックしてイベント リスニングをトリガーします。ストレージからキャッシュ内のトークンを取得します。
- バックエンド インターフェイスを呼び出して、ユーザーのニックネームやその他の情報など、トークンに基づいてユーザーのログイン情報を交換します。
- 追伸:
- 新しいバージョンの WeChat アプレット APi WeChat ニックネームは直接取得をサポートしなくなったことに注意してください。
- WeChat ニックネームを取得する必要がある場合は、ユーザー センターで通話インターフェイスをプルダウンして取得する必要があるため、バックエンドで直接デフォルトのニックネームにアップグレードすることをお勧めします。
- 将来的には、ユーザーはニックネーム、携帯電話番号、アバターなどの情報をユーザーセンターで変更できるようになります。
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の変更
インデックス.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の変更
インデックス.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 |
WeChat アプレットは開発者サーバーのログイン インターフェイスを呼び出します |
/wxMiniProgramAppService/checkWxMiniProgramLogin.do |
WeChat アプレットは開発者サーバーを呼び出して、ログインしているかどうか、およびトークンが有効かどうかを確認します。 |
/wxMiniProgramAppService/fetchWxUserInfo.do |
WeChat アプレットは開発者サーバーを呼び出してユーザー情報インターフェイスを取得します |
1.3.2.1 WeChat アプレットのログイン関連インターフェイス層
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 アプレットのログイン関連サービス インターフェイス層
/***
* @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アプレットログイン関連サービス実装クラス層
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 アプレット サービス インターフェイス クラス
/***
* @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 アプレット サービス インターフェイス実装クラス
/***
* @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 このセクションのソースコードをダウンロードする
- WeChat アプレット フロントエンド ソース コードjavascript-base-wx-login-sample
- WeChat アプレット バックエンド ソース コードwx-mini-program-proxy