hookehuyr

feat(auth): 实现静默授权并优化授权流程

重构授权处理逻辑,新增静默授权功能避免页面跳转
优化401错误处理,使用静默授权替代页面跳转
移除未加入家庭时的强制跳转逻辑
重构returnToOriginalPage函数,优化页面跳转逻辑
......@@ -336,10 +336,6 @@ useDidShow(async () => {
if (rankingCardRef.value) {
rankingCardRef.value.refreshData();
}
} else {
// 如果没有加入家庭(code为0),跳转到欢迎页面
Taro.redirectTo({ url: '/pages/Welcome/index' });
return; // 直接返回,不执行后续逻辑
}
// TODO: 获取广告信息
......
<!--
* @Date: 2022-09-19 14:11:06
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-09-07 01:37:03
* @LastEditTime: 2025-09-12 10:20:03
* @FilePath: /lls_program/src/pages/auth/index.vue
* @Description: 文件描述
-->
......@@ -22,72 +22,13 @@ import request from '@/utils/request';
<script>
import "./index.less";
import { getCurrentPageParam } from "@/utils/weapp";
import { returnToOriginalPage } from "@/utils/authRedirect";
import { returnToOriginalPage, silentAuth } from '@/utils/authRedirect'
export default {
name: "authPage",
mounted () {
// 授权登陆
Taro.login({
success: function (res) {
if (res.code) {
//发起网络请求
Taro.showLoading({
title: '授权中',
})
// 根据环境判断是否传递openid参数
const requestData = {
code: res.code,
};
// 测试环境下传递openid,正式环境不传递
if (process.env.NODE_ENV === 'development') {
requestData.openid = 'h-008';
// requestData.openid = 'h-009';
// requestData.openid = 'h-010';
// requestData.openid = 'h-011';
// requestData.openid = 'h-012';
// requestData.openid = 'h-013';
// requestData.openid = 'oWbdFvkD5VtloC50wSNR9IWiU2q8';
// requestData.openid = 'oex8h5QZnZJto3ttvO6swSvylAQo';
}
request.post('/srv/?a=openid', requestData)
.then(res => {
if (res.data.code) {
var cookie = res.cookies[0];
if (cookie != null) {
wx.setStorageSync("sessionid", res.cookies[0]);//服务器返回的 Set-Cookie,保存到本地
//TAG 小程序绑定cookie
// 修改请求头
request.defaults.headers.cookie = res.cookies[0];
// if (res.data.data.avatar) {
// Taro.reLaunch({
// url: '../../' + getCurrentPageParam().url
// })
// } else { // 头像没有设置跳转完善信息页面
// Taro.redirectTo({
// url: '../apxUserInfo/index'
// })
// }
// TAG:处理分享跳转问题 - 使用新的重定向逻辑
returnToOriginalPage();
Taro.hideLoading();
}
} else {
console.warn(res.data.msg);
Taro.hideLoading();
}
})
.catch(err => {
console.error(err);
Taro.hideLoading();
});
} else {
console.log('登录失败!' + res.errMsg)
}
}
})
mounted() {
// 页面加载时优先尝试静默授权
this.performAuth();
},
data () {
return {
......@@ -110,6 +51,77 @@ export default {
// })
},
methods: {
performAuth() {
// 优先尝试静默授权
silentAuth().then(() => {
// 静默授权成功,检查当前页面是否需要跳转
const pages = Taro.getCurrentPages();
const currentPage = pages[pages.length - 1];
const currentRoute = currentPage?.route;
// 只有在auth页面时才需要跳转回原页面
if (currentRoute === 'pages/auth/index') {
returnToOriginalPage();
}
}).catch(() => {
// 静默授权失败,执行手动授权
this.manualAuth();
});
},
manualAuth() {
// 手动授权登陆
Taro.login({
success: function (res) {
if (res.code) {
//发起网络请求
Taro.showLoading({
title: '授权中',
})
// 根据环境判断是否传递openid参数
const requestData = {
code: res.code,
};
// 测试环境下传递openid,正式环境不传递
if (process.env.NODE_ENV === 'development') {
// requestData.openid = 'h-008';
// requestData.openid = 'h-009';
// requestData.openid = 'h-010';
// requestData.openid = 'h-011';
// requestData.openid = 'h-012';
// requestData.openid = 'h-013';
// requestData.openid = 'oWbdFvkD5VtloC50wSNR9IWiU2q8';
requestData.openid = 'oex8h5QZnZJto3ttvO6swSvylAQo';
}
request.post('/srv/?a=openid', requestData)
.then(res => {
if (res.data.code) {
var cookie = res.cookies[0];
if (cookie != null) {
wx.setStorageSync("sessionid", res.cookies[0]);//服务器返回的 Set-Cookie,保存到本地
//TAG 小程序绑定cookie
// 修改请求头
request.defaults.headers.cookie = res.cookies[0];
// TAG:处理分享跳转问题 - 使用新的重定向逻辑
returnToOriginalPage();
Taro.hideLoading();
}
} else {
console.warn(res.data.msg);
Taro.hideLoading();
}
})
.catch(err => {
console.error(err);
Taro.hideLoading();
});
} else {
console.log('登录失败!' + res.errMsg)
}
}
})
},
bindGetUserInfo (e) {
console.warn(e.detail.userInfo)
},
......
/*
* @Date: 2025-01-25 10:00:00
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-09-04 15:33:00
* @LastEditTime: 2025-09-12 10:52:09
* @FilePath: /lls_program/src/utils/authRedirect.js
* @Description: 授权重定向处理工具函数
*/
import Taro from '@tarojs/taro'
import { routerStore } from '@/stores/router'
import request from '@/utils/request'
import { getMyFamiliesAPI } from '@/api/family'
/**
* 获取当前页面完整路径(包含参数)
......@@ -61,128 +63,91 @@ export const navigateToAuth = (returnPath) => {
* @param {string} defaultPath - 默认返回路径,如果没有保存的路径则使用此路径
*/
/**
* 返回到原始页面,带有容错处理机制
* 检查用户是否已加入家庭
* @returns {Promise<boolean>} 返回是否已加入家庭
*/
export const checkUserHasFamily = async () => {
try {
const { code, data } = await getMyFamiliesAPI()
if (code && data && data.length > 0) {
return true
}
return false
} catch (error) {
console.error('检查用户家庭状态失败:', error)
return false
}
}
/**
* 返回到原始页面,优化版本避免重复跳转
* @param {string} defaultPath - 默认跳转路径
* @param {number} retryCount - 重试次数,默认为2
*/
export const returnToOriginalPage = async (defaultPath = '/pages/Dashboard/index', retryCount = 2) => {
export const returnToOriginalPage = async (defaultPath = '/pages/Dashboard/index') => {
const router = routerStore()
const savedPath = router.url
/**
* 执行页面跳转的核心函数
* @param {string} targetPath - 目标路径
* @param {boolean} isHomePage - 是否为首页
*/
const executeNavigation = async (targetPath, isHomePage = false) => {
try {
if (isHomePage) {
await Taro.reLaunch({
url: targetPath
})
} else {
await Taro.redirectTo({
url: targetPath
})
}
return true
} catch (error) {
console.warn('页面跳转失败:', error)
return false
try {
// 清除保存的路径
router.remove()
// 获取当前页面栈
const pages = Taro.getCurrentPages()
const currentPage = pages[pages.length - 1]
const currentRoute = currentPage?.route
// 确定目标路径
let targetPath = defaultPath
if (savedPath && savedPath !== '') {
targetPath = savedPath.startsWith('/') ? savedPath : `/${savedPath}`
}
}
/**
* 带重试机制的页面跳转
* @param {string} targetPath - 目标路径
* @param {boolean} isHomePage - 是否为首页
* @param {number} maxRetries - 最大重试次数
*/
const navigateWithRetry = async (targetPath, isHomePage, maxRetries) => {
for (let i = 0; i <= maxRetries; i++) {
const success = await executeNavigation(targetPath, isHomePage)
if (success) {
return true
}
// 检查用户是否已加入家庭
const hasFamily = await checkUserHasFamily()
// 如果不是最后一次重试,等待一段时间后重试
if (i < maxRetries) {
console.warn(`页面跳转重试 ${i + 1}/${maxRetries}`)
await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1))) // 递增延迟
}
// 如果用户没有加入家庭,跳转到欢迎页面
if (!hasFamily) {
await Taro.reLaunch({
url: '/pages/Welcome/index'
})
return
}
return false
}
try {
if (savedPath && savedPath !== '') {
// 清除保存的路径
router.remove()
const targetUrl = `/${savedPath}`
const isHomePage = savedPath.includes('Dashboard/index') || savedPath === 'pages/Dashboard/index'
// 尝试跳转到保存的页面
const success = await navigateWithRetry(targetUrl, isHomePage, retryCount)
if (!success) {
console.warn('跳转到保存页面失败,尝试跳转到默认页面')
// 如果跳转失败,尝试跳转到默认页面
const fallbackSuccess = await navigateWithRetry(defaultPath, true, retryCount)
if (!fallbackSuccess) {
// 最后的降级方案:使用 switchTab 跳转到首页
console.warn('所有跳转方式失败,使用 switchTab 降级方案')
try {
await Taro.switchTab({
url: '/pages/Dashboard/index'
})
} catch (switchError) {
console.error('switchTab 也失败了:', switchError)
// 显示用户友好的错误提示
Taro.showToast({
title: '页面跳转失败,请重新打开小程序',
icon: 'none',
duration: 3000
})
}
}
}
// 提取目标页面路由(去掉参数)
const targetRoute = targetPath.split('?')[0].replace(/^\//, '')
// 如果当前页面就是目标页面,不需要跳转
if (currentRoute === targetRoute) {
return
}
// 如果目标是首页,使用 reLaunch
if (targetRoute === 'pages/Dashboard/index') {
await Taro.reLaunch({
url: '/pages/Dashboard/index'
})
} else {
// 没有保存的路径,直接跳转到默认页面
const success = await navigateWithRetry(defaultPath, true, retryCount)
if (!success) {
// 降级方案
console.warn('跳转到默认页面失败,使用 switchTab 降级方案')
try {
await Taro.switchTab({
url: '/pages/Dashboard/index'
})
} catch (switchError) {
console.error('switchTab 也失败了:', switchError)
Taro.showToast({
title: '页面跳转失败,请重新打开小程序',
icon: 'none',
duration: 3000
})
}
}
// 其他页面使用 redirectTo
await Taro.redirectTo({
url: targetPath
})
}
} catch (error) {
console.error('returnToOriginalPage 执行出错:', error)
// 最终的错误处理
// 错误处理:检查是否有家庭,决定跳转到哪里
try {
await Taro.switchTab({
url: '/pages/Dashboard/index'
})
const hasFamily = await checkUserHasFamily()
if (hasFamily) {
await Taro.reLaunch({
url: '/pages/Dashboard/index'
})
} else {
await Taro.reLaunch({
url: '/pages/Welcome/index'
})
}
} catch (finalError) {
console.error('最终降级方案也失败了:', finalError)
Taro.showToast({
title: '系统异常,请重新打开小程序',
icon: 'none',
duration: 3000
})
}
}
}
......@@ -235,3 +200,139 @@ export const addShareFlag = (path) => {
const separator = path.includes('?') ? '&' : '?'
return `${path}${separator}from_share=1`
}
/**
* 静默授权处理函数
* 在后台处理授权,不跳转页面,避免用户感知
* @param {Function} onSuccess - 授权成功回调
* @param {Function} onError - 授权失败回调
* @returns {Promise} 授权结果
*/
export const silentAuth = async (onSuccess, onError) => {
return new Promise((resolve, reject) => {
// 检查是否已经授权
if (!needAuth()) {
// 已经授权,直接返回成功
if (onSuccess) {
onSuccess({ code: 1, msg: '已授权' })
}
resolve({ code: 1, msg: '已授权' })
return
}
// 显示loading提示
Taro.showLoading({
title: '加载中...',
mask: true
})
// 调用微信登录
Taro.login({
success: function (res) {
if (res.code) {
// 构建请求数据
const requestData = {
code: res.code,
}
// 测试环境下传递openid,正式环境不传递
if (process.env.NODE_ENV === 'development') {
requestData.openid = 'h-008';
// requestData.openid = 'h-009';
// requestData.openid = 'h-010';
// requestData.openid = 'h-011';
// requestData.openid = 'h-012';
// requestData.openid = 'h-013';
// requestData.openid = 'oWbdFvkD5VtloC50wSNR9IWiU2q8';
// requestData.openid = 'oex8h5QZnZJto3ttvO6swSvylAQo';
}
// 发起授权请求 - 使用request.post保持与手动授权一致
request.post('/srv/?a=openid', requestData)
.then(response => {
Taro.hideLoading()
if (response.data.code) {
const cookie = response.cookies && response.cookies[0]
if (cookie) {
// 保存sessionid到本地存储
wx.setStorageSync("sessionid", cookie)
// 更新request默认headers
request.defaults.headers.cookie = cookie
// 静默授权成功
// 执行成功回调
if (onSuccess) {
onSuccess(response.data)
}
resolve(response.data)
} else {
console.error('授权响应中没有cookie信息')
if (onError) {
onError('授权失败:没有获取到有效的会话信息')
}
reject(new Error('授权失败:没有获取到有效的会话信息'))
}
} else {
console.error('静默授权失败:', response.data.msg)
if (onError) {
onError(response.data.msg || '授权失败')
}
reject(new Error(response.data.msg || '授权失败'))
}
})
.catch(error => {
console.error('静默授权请求失败:', error)
Taro.hideLoading()
if (onError) {
onError('网络请求失败,请稍后重试')
}
reject(new Error('授权失败,请稍后重试'))
})
} else {
console.error('微信登录失败:', res.errMsg)
Taro.hideLoading()
if (onError) {
onError('微信登录失败:' + res.errMsg)
}
reject(new Error('微信登录失败:' + res.errMsg))
}
},
fail: (error) => {
console.error('调用微信登录失败:', error)
Taro.hideLoading()
if (onError) {
onError('调用微信登录失败')
}
reject(error)
}
})
})
}
/**
* 检查是否需要授权
* @returns {boolean} 是否需要授权
*/
export const needAuth = () => {
try {
const sessionid = wx.getStorageSync("sessionid")
return !sessionid || sessionid === ''
} catch (error) {
console.error('检查授权状态失败:', error)
return true
}
}
......
/*
* @Date: 2022-09-19 14:11:06
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-09-04 15:33:42
* @LastEditTime: 2025-09-12 10:44:34
* @FilePath: /lls_program/src/utils/request.js
* @Description: 简单axios封装,后续按实际处理
*/
// import axios from 'axios'
import axios from 'axios-miniprogram';
// // import qs from 'Qs'
import { navigateToAuth } from '@/utils/authRedirect'
import { silentAuth } from '@/utils/authRedirect'
// import { ProgressStart, ProgressEnd } from '@/components/axios-progress/progress';
// import store from '@/store'
......@@ -147,18 +147,28 @@ service.interceptors.response.use(
/**
* 处理401未授权状态
* 清除本地sessionid并跳转到登录页
* 清除本地sessionid并使用静默授权
*/
if (response.data.code === 401) {
// 清除无效的sessionid
clearSessionId();
/**
* 未授权跳转登录页
* 授权完成后 返回当前页面
*/
setTimeout(() => {
navigateToAuth();
}, 1000);
// 使用静默授权处理,避免页面跳转
return silentAuth(
() => {
// 授权成功后重新发起原始请求
const originalRequest = response.config;
return service.request(originalRequest);
},
(error) => {
// 静默授权失败,直接返回错误,不跳转页面
console.error('静默授权失败:', error);
return Promise.reject(new Error('授权失败,请稍后重试'));
}
).catch(() => {
// 如果静默授权完全失败,直接返回错误,不跳转页面
return Promise.reject(new Error('授权失败,请稍后重试'));
});
}
return response
},
......