微信小程序获取手机号一键登录偶尔会失败?

797 阅读2分钟

直接上代码,先看下优化前的代码

<button
    class="button"
    open-type="getPhoneNumber"
    @getphonenumber="getPhoneNumber"
>
    一键登录
</button>
function promisify<type>(method, options?): Promise<type> {
    return new Promise((resolve, reject) => {
        // 将options对象赋值 然后再传给下面调用的方法中
        options = options || {};
        options.success = resolve;
        options.fail = reject;
        uni[method](options);
    });
}
// 一键获取手机号登录
const getPhoneNumber = async(e) => {
    const {
        encryptedData,
        iv,
        errMsg,
    } = e.detail;

    if (errMsg == 'getPhoneNumber:fail:user deny'
        || errMsg == 'getPhoneNumber:fail user deny'
        || errMsg == 'getPhoneNumber:fail:user cancel'
    ) {
        return ‘refuse’; // 用户拒绝授权
    }

    if (errMsg === 'getPhoneNumber:ok') {
        try {
            const { code } = await promisify<{code: string}>('login');
            await requestLogin({
                iv,
                encryptedData,
                js_code: code,
            });
            await getUserInfo();
            return 'success';// 登录成功
        } catch (err) {
            console.log('登录失败', err);
            return 'fail'; // 登录失败
        }
    }
};

部署后发现每天早晨第一次点击“一键登录按钮”的时候会出现登录失败的情况,经过研究发现

复现步骤:多次点击”一键登录“按钮但是授权弹窗中点击“不允许”按钮,最后再点击“手机号”授权发起请求

调研

查看getPhoneNumber官方文档发现

在getPhoneNumber回调中调用 wx.login 登录,可能会刷新登录态。此时服务器使用wx.login得到的code 换取的 sessionKey 不是加密手机号时使用的 sessionKey,导致解密失败。建议开发者提前进行 login;或者在回调中先使用 checkSession 进行登录态检查,避免 login 刷新登录态。

优化后的代码

html增加点击事件,在用户在弹窗中授予手机号权限之前先执行点击事件获取微信登录code

<button
    class="button"
    open-type="getPhoneNumber"
    @getphonenumber="getPhoneNumber"
    @click="getWxLoginCode"
>
    一键登录
</button>
function promisify<type>(method, options?): Promise<type> {
    return new Promise((resolve, reject) => {
        // 将options对象赋值 然后再传给下面调用的方法中
        options = options || {};
        options.success = resolve;
        options.fail = reject;
        uni[method](options);
    });
}

const js_code = ref("");
// 通过微信login方法获取code
const getWxLoginCode = async() => {
    console.log('getWxLoginCode');
    try {
        const {code} = await promisify<{code: string}>('login');
        js_code.value = code;
    } catch (error) {
        console.log('getWxLoginCode:', error);
    }
};

// 使用 `checkSession` 进行登录态检查
const checkSession = () => promisify('checkSession').then(() => true).catch(() => false);

// 一键获取手机号登录
const getPhoneNumber = async(e) => {
    const loginState = await checkSession();
    console.log('loginState:', loginState);
    if(!loginState) {
        await getWxLoginCode();
    }
    const {
        encryptedData,
        iv,
        errMsg,
    } = e.detail;

    if (errMsg == 'getPhoneNumber:fail:user deny'
        || errMsg == 'getPhoneNumber:fail user deny'
        || errMsg == 'getPhoneNumber:fail:user cancel'
    ) {
        return ‘refuse’; // 用户拒绝授权
    }

    if (errMsg === 'getPhoneNumber:ok') {
        try {
            await requestLogin({
                iv,
                encryptedData,
                js_code: js_code.value,
            });
            await getUserInfo();
            return 'success';// 登录成功
        } catch (err) {
            console.log('登录失败', err);
            return 'fail'; // 登录失败
        }
    }
};

现在获取微信登录code时机是早于获取电话号码的,当用code获取的解密信息去解密手机号加密信息iv、encryptedData时是可以解密的。

每次wx.login都会更新解密信息,并且解密信息会在微信服务器保存一段时间,要确保解密成功,一定要用获取加密信息之前wx.login时获取的解密信息进行解密。

参考

小程序中wx.login与获取用户信息调用的时机