前端接入微信登录授权方案
实际为通过微信提供的getPhoneNumbe
方法拿到iv,encryptedData
,与用wx.login()
方法获取的code。传给服务端。服务端需要在后台服务器调用code2Session,使用code换取openid、unionid、session_key等系信息,解密iv,encryptedData
,从而获得手机号。
服务端登录之后把token和手机号返回给前端,缓存后下次token失效可以直接用手机号登录。
登录按钮
由于微信更新了用户隐私规范,授权窗口只能通过规定的按钮由用户点击触发,无法通过api的方式调起,所以我们需要封装一个登录按钮。
注意:1、在回调中调用 wx.login
登录,可能会刷新登录态。此时服务器使用 code 换取的 sessionKey 不是加密时使用的 sessionKey,导致解密失败。
2、wx.login()获取的code只有5分钟有效期,如果用户在获取手机号五分钟后点击登录会失效。
所以在点击获取手机号的时候调wx.login()
import { BaseEventOrig, Button, ButtonProps } from "@tarojs/components";
import React, { useState } from "react";
import Taro from "@tarojs/taro";
import { getLoginCode } from "@/common/api/user";
import { miniProgramAuthLogin } from "@/common/api/login";
// LoginButton.ts
const LoginButton = React.memo((props: any) => {
const { children, onSuccess, onClick, ...other } = props;
const [code, setCode] = useState("");
const handleGetPhoneNumber = async (
e: BaseEventOrig<ButtonProps.onGetPhoneNumberEventDetail>
) => {
console.log('e.detail ', e.detail)
const { iv, encryptedData, errMsg } = e.detail;
try {
if (!code) {
// 实际上是调用了wx.Login()
getLoginCode().then((wxCode: any) => {
Taro.showToast({ title: wxCode });
setCode(wxCode);
if (errMsg.includes("ok")) {
Taro.showLoading({ title: "正在登录" });
miniProgramAuthLogin({
iv,
encryptedData,
loginCode: wxCode,
getMobileCode: e.detail.code
}).then(res => {
Taro.hideLoading();
onSuccess?.(res);
Taro.showToast({ title: '登录成功' });
});
}
});
}
} catch (error) {
// 解密失败重新login
getLoginCode().then((wxCode: any) => {
setCode(wxCode);
});
Taro.hideLoading();
Taro.showToast({ title: error.message || error.errMsg });
}
};
const handleBtnClick = async (event) => {
onClick?.(event);
};
return (
<>
<Button
openType="getPhoneNumber" // 用于将 code 换取用户手机号
onClick={handleBtnClick}
onGetPhoneNumber={handleGetPhoneNumber}
{...other}
>
{children}
</Button>
</>
);
});
export default LoginButton;
登录方法
import Taro from "@tarojs/taro";
// 使用promise封装微信开放接口login
export const getLoginCode = () => {
return new Promise((resolve, reject) => {
Taro.login({
success: function (res) {
if (res.code) {
resolve(res.code)
} else {
console.log('登录失败!' + res.errMsg)
reject(res)
}
}
})
})
}