hookehuyr

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

重构授权处理逻辑,新增静默授权功能避免页面跳转
优化401错误处理,使用静默授权替代页面跳转
移除未加入家庭时的强制跳转逻辑
重构returnToOriginalPage函数,优化页面跳转逻辑
...@@ -336,10 +336,6 @@ useDidShow(async () => { ...@@ -336,10 +336,6 @@ useDidShow(async () => {
336 if (rankingCardRef.value) { 336 if (rankingCardRef.value) {
337 rankingCardRef.value.refreshData(); 337 rankingCardRef.value.refreshData();
338 } 338 }
339 - } else {
340 - // 如果没有加入家庭(code为0),跳转到欢迎页面
341 - Taro.redirectTo({ url: '/pages/Welcome/index' });
342 - return; // 直接返回,不执行后续逻辑
343 } 339 }
344 340
345 // TODO: 获取广告信息 341 // TODO: 获取广告信息
......
1 <!-- 1 <!--
2 * @Date: 2022-09-19 14:11:06 2 * @Date: 2022-09-19 14:11:06
3 * @LastEditors: hookehuyr hookehuyr@gmail.com 3 * @LastEditors: hookehuyr hookehuyr@gmail.com
4 - * @LastEditTime: 2025-09-07 01:37:03 4 + * @LastEditTime: 2025-09-12 10:20:03
5 * @FilePath: /lls_program/src/pages/auth/index.vue 5 * @FilePath: /lls_program/src/pages/auth/index.vue
6 * @Description: 文件描述 6 * @Description: 文件描述
7 --> 7 -->
...@@ -22,72 +22,13 @@ import request from '@/utils/request'; ...@@ -22,72 +22,13 @@ import request from '@/utils/request';
22 <script> 22 <script>
23 import "./index.less"; 23 import "./index.less";
24 import { getCurrentPageParam } from "@/utils/weapp"; 24 import { getCurrentPageParam } from "@/utils/weapp";
25 -import { returnToOriginalPage } from "@/utils/authRedirect"; 25 +import { returnToOriginalPage, silentAuth } from '@/utils/authRedirect'
26 26
27 export default { 27 export default {
28 name: "authPage", 28 name: "authPage",
29 - mounted () { 29 + mounted() {
30 - // 授权登陆 30 + // 页面加载时优先尝试静默授权
31 - Taro.login({ 31 + this.performAuth();
32 - success: function (res) {
33 - if (res.code) {
34 - //发起网络请求
35 - Taro.showLoading({
36 - title: '授权中',
37 - })
38 - // 根据环境判断是否传递openid参数
39 - const requestData = {
40 - code: res.code,
41 - };
42 -
43 - // 测试环境下传递openid,正式环境不传递
44 - if (process.env.NODE_ENV === 'development') {
45 - requestData.openid = 'h-008';
46 - // requestData.openid = 'h-009';
47 - // requestData.openid = 'h-010';
48 - // requestData.openid = 'h-011';
49 - // requestData.openid = 'h-012';
50 - // requestData.openid = 'h-013';
51 - // requestData.openid = 'oWbdFvkD5VtloC50wSNR9IWiU2q8';
52 - // requestData.openid = 'oex8h5QZnZJto3ttvO6swSvylAQo';
53 - }
54 -
55 - request.post('/srv/?a=openid', requestData)
56 - .then(res => {
57 - if (res.data.code) {
58 - var cookie = res.cookies[0];
59 - if (cookie != null) {
60 - wx.setStorageSync("sessionid", res.cookies[0]);//服务器返回的 Set-Cookie,保存到本地
61 - //TAG 小程序绑定cookie
62 - // 修改请求头
63 - request.defaults.headers.cookie = res.cookies[0];
64 - // if (res.data.data.avatar) {
65 - // Taro.reLaunch({
66 - // url: '../../' + getCurrentPageParam().url
67 - // })
68 - // } else { // 头像没有设置跳转完善信息页面
69 - // Taro.redirectTo({
70 - // url: '../apxUserInfo/index'
71 - // })
72 - // }
73 - // TAG:处理分享跳转问题 - 使用新的重定向逻辑
74 - returnToOriginalPage();
75 - Taro.hideLoading();
76 - }
77 - } else {
78 - console.warn(res.data.msg);
79 - Taro.hideLoading();
80 - }
81 - })
82 - .catch(err => {
83 - console.error(err);
84 - Taro.hideLoading();
85 - });
86 - } else {
87 - console.log('登录失败!' + res.errMsg)
88 - }
89 - }
90 - })
91 }, 32 },
92 data () { 33 data () {
93 return { 34 return {
...@@ -110,6 +51,77 @@ export default { ...@@ -110,6 +51,77 @@ export default {
110 // }) 51 // })
111 }, 52 },
112 methods: { 53 methods: {
54 + performAuth() {
55 + // 优先尝试静默授权
56 + silentAuth().then(() => {
57 + // 静默授权成功,检查当前页面是否需要跳转
58 + const pages = Taro.getCurrentPages();
59 + const currentPage = pages[pages.length - 1];
60 + const currentRoute = currentPage?.route;
61 +
62 + // 只有在auth页面时才需要跳转回原页面
63 + if (currentRoute === 'pages/auth/index') {
64 + returnToOriginalPage();
65 + }
66 + }).catch(() => {
67 + // 静默授权失败,执行手动授权
68 + this.manualAuth();
69 + });
70 + },
71 + manualAuth() {
72 + // 手动授权登陆
73 + Taro.login({
74 + success: function (res) {
75 + if (res.code) {
76 + //发起网络请求
77 + Taro.showLoading({
78 + title: '授权中',
79 + })
80 + // 根据环境判断是否传递openid参数
81 + const requestData = {
82 + code: res.code,
83 + };
84 +
85 + // 测试环境下传递openid,正式环境不传递
86 + if (process.env.NODE_ENV === 'development') {
87 + // requestData.openid = 'h-008';
88 + // requestData.openid = 'h-009';
89 + // requestData.openid = 'h-010';
90 + // requestData.openid = 'h-011';
91 + // requestData.openid = 'h-012';
92 + // requestData.openid = 'h-013';
93 + // requestData.openid = 'oWbdFvkD5VtloC50wSNR9IWiU2q8';
94 + requestData.openid = 'oex8h5QZnZJto3ttvO6swSvylAQo';
95 + }
96 +
97 + request.post('/srv/?a=openid', requestData)
98 + .then(res => {
99 + if (res.data.code) {
100 + var cookie = res.cookies[0];
101 + if (cookie != null) {
102 + wx.setStorageSync("sessionid", res.cookies[0]);//服务器返回的 Set-Cookie,保存到本地
103 + //TAG 小程序绑定cookie
104 + // 修改请求头
105 + request.defaults.headers.cookie = res.cookies[0];
106 + // TAG:处理分享跳转问题 - 使用新的重定向逻辑
107 + returnToOriginalPage();
108 + Taro.hideLoading();
109 + }
110 + } else {
111 + console.warn(res.data.msg);
112 + Taro.hideLoading();
113 + }
114 + })
115 + .catch(err => {
116 + console.error(err);
117 + Taro.hideLoading();
118 + });
119 + } else {
120 + console.log('登录失败!' + res.errMsg)
121 + }
122 + }
123 + })
124 + },
113 bindGetUserInfo (e) { 125 bindGetUserInfo (e) {
114 console.warn(e.detail.userInfo) 126 console.warn(e.detail.userInfo)
115 }, 127 },
......
1 /* 1 /*
2 * @Date: 2025-01-25 10:00:00 2 * @Date: 2025-01-25 10:00:00
3 * @LastEditors: hookehuyr hookehuyr@gmail.com 3 * @LastEditors: hookehuyr hookehuyr@gmail.com
4 - * @LastEditTime: 2025-09-04 15:33:00 4 + * @LastEditTime: 2025-09-12 10:52:09
5 * @FilePath: /lls_program/src/utils/authRedirect.js 5 * @FilePath: /lls_program/src/utils/authRedirect.js
6 * @Description: 授权重定向处理工具函数 6 * @Description: 授权重定向处理工具函数
7 */ 7 */
8 import Taro from '@tarojs/taro' 8 import Taro from '@tarojs/taro'
9 import { routerStore } from '@/stores/router' 9 import { routerStore } from '@/stores/router'
10 +import request from '@/utils/request'
11 +import { getMyFamiliesAPI } from '@/api/family'
10 12
11 /** 13 /**
12 * 获取当前页面完整路径(包含参数) 14 * 获取当前页面完整路径(包含参数)
...@@ -61,128 +63,91 @@ export const navigateToAuth = (returnPath) => { ...@@ -61,128 +63,91 @@ export const navigateToAuth = (returnPath) => {
61 * @param {string} defaultPath - 默认返回路径,如果没有保存的路径则使用此路径 63 * @param {string} defaultPath - 默认返回路径,如果没有保存的路径则使用此路径
62 */ 64 */
63 /** 65 /**
64 - * 返回到原始页面,带有容错处理机制 66 + * 检查用户是否已加入家庭
67 + * @returns {Promise<boolean>} 返回是否已加入家庭
68 + */
69 +export const checkUserHasFamily = async () => {
70 + try {
71 + const { code, data } = await getMyFamiliesAPI()
72 + if (code && data && data.length > 0) {
73 + return true
74 + }
75 + return false
76 + } catch (error) {
77 + console.error('检查用户家庭状态失败:', error)
78 + return false
79 + }
80 +}
81 +
82 +/**
83 + * 返回到原始页面,优化版本避免重复跳转
65 * @param {string} defaultPath - 默认跳转路径 84 * @param {string} defaultPath - 默认跳转路径
66 - * @param {number} retryCount - 重试次数,默认为2
67 */ 85 */
68 -export const returnToOriginalPage = async (defaultPath = '/pages/Dashboard/index', retryCount = 2) => { 86 +export const returnToOriginalPage = async (defaultPath = '/pages/Dashboard/index') => {
69 const router = routerStore() 87 const router = routerStore()
70 const savedPath = router.url 88 const savedPath = router.url
71 89
72 - /** 90 + try {
73 - * 执行页面跳转的核心函数 91 + // 清除保存的路径
74 - * @param {string} targetPath - 目标路径 92 + router.remove()
75 - * @param {boolean} isHomePage - 是否为首页 93 +
76 - */ 94 + // 获取当前页面栈
77 - const executeNavigation = async (targetPath, isHomePage = false) => { 95 + const pages = Taro.getCurrentPages()
78 - try { 96 + const currentPage = pages[pages.length - 1]
79 - if (isHomePage) { 97 + const currentRoute = currentPage?.route
80 - await Taro.reLaunch({ 98 +
81 - url: targetPath 99 + // 确定目标路径
82 - }) 100 + let targetPath = defaultPath
83 - } else { 101 + if (savedPath && savedPath !== '') {
84 - await Taro.redirectTo({ 102 + targetPath = savedPath.startsWith('/') ? savedPath : `/${savedPath}`
85 - url: targetPath
86 - })
87 - }
88 - return true
89 - } catch (error) {
90 - console.warn('页面跳转失败:', error)
91 - return false
92 } 103 }
93 - }
94 104
95 - /** 105 + // 检查用户是否已加入家庭
96 - * 带重试机制的页面跳转 106 + const hasFamily = await checkUserHasFamily()
97 - * @param {string} targetPath - 目标路径
98 - * @param {boolean} isHomePage - 是否为首页
99 - * @param {number} maxRetries - 最大重试次数
100 - */
101 - const navigateWithRetry = async (targetPath, isHomePage, maxRetries) => {
102 - for (let i = 0; i <= maxRetries; i++) {
103 - const success = await executeNavigation(targetPath, isHomePage)
104 - if (success) {
105 - return true
106 - }
107 107
108 - // 如果不是最后一次重试,等待一段时间后重试 108 + // 如果用户没有加入家庭,跳转到欢迎页面
109 - if (i < maxRetries) { 109 + if (!hasFamily) {
110 - console.warn(`页面跳转重试 ${i + 1}/${maxRetries}`) 110 + await Taro.reLaunch({
111 - await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1))) // 递增延迟 111 + url: '/pages/Welcome/index'
112 - } 112 + })
113 + return
113 } 114 }
114 - return false
115 - }
116 115
117 - try { 116 + // 提取目标页面路由(去掉参数)
118 - if (savedPath && savedPath !== '') { 117 + const targetRoute = targetPath.split('?')[0].replace(/^\//, '')
119 - // 清除保存的路径 118 +
120 - router.remove() 119 + // 如果当前页面就是目标页面,不需要跳转
121 - 120 + if (currentRoute === targetRoute) {
122 - const targetUrl = `/${savedPath}` 121 + return
123 - const isHomePage = savedPath.includes('Dashboard/index') || savedPath === 'pages/Dashboard/index' 122 + }
124 - 123 +
125 - // 尝试跳转到保存的页面 124 + // 如果目标是首页,使用 reLaunch
126 - const success = await navigateWithRetry(targetUrl, isHomePage, retryCount) 125 + if (targetRoute === 'pages/Dashboard/index') {
127 - 126 + await Taro.reLaunch({
128 - if (!success) { 127 + url: '/pages/Dashboard/index'
129 - console.warn('跳转到保存页面失败,尝试跳转到默认页面') 128 + })
130 - // 如果跳转失败,尝试跳转到默认页面
131 - const fallbackSuccess = await navigateWithRetry(defaultPath, true, retryCount)
132 -
133 - if (!fallbackSuccess) {
134 - // 最后的降级方案:使用 switchTab 跳转到首页
135 - console.warn('所有跳转方式失败,使用 switchTab 降级方案')
136 - try {
137 - await Taro.switchTab({
138 - url: '/pages/Dashboard/index'
139 - })
140 - } catch (switchError) {
141 - console.error('switchTab 也失败了:', switchError)
142 - // 显示用户友好的错误提示
143 - Taro.showToast({
144 - title: '页面跳转失败,请重新打开小程序',
145 - icon: 'none',
146 - duration: 3000
147 - })
148 - }
149 - }
150 - }
151 } else { 129 } else {
152 - // 没有保存的路径,直接跳转到默认页面 130 + // 其他页面使用 redirectTo
153 - const success = await navigateWithRetry(defaultPath, true, retryCount) 131 + await Taro.redirectTo({
154 - 132 + url: targetPath
155 - if (!success) { 133 + })
156 - // 降级方案
157 - console.warn('跳转到默认页面失败,使用 switchTab 降级方案')
158 - try {
159 - await Taro.switchTab({
160 - url: '/pages/Dashboard/index'
161 - })
162 - } catch (switchError) {
163 - console.error('switchTab 也失败了:', switchError)
164 - Taro.showToast({
165 - title: '页面跳转失败,请重新打开小程序',
166 - icon: 'none',
167 - duration: 3000
168 - })
169 - }
170 - }
171 } 134 }
172 } catch (error) { 135 } catch (error) {
173 console.error('returnToOriginalPage 执行出错:', error) 136 console.error('returnToOriginalPage 执行出错:', error)
174 - // 最终的错误处理 137 + // 错误处理:检查是否有家庭,决定跳转到哪里
175 try { 138 try {
176 - await Taro.switchTab({ 139 + const hasFamily = await checkUserHasFamily()
177 - url: '/pages/Dashboard/index' 140 + if (hasFamily) {
178 - }) 141 + await Taro.reLaunch({
142 + url: '/pages/Dashboard/index'
143 + })
144 + } else {
145 + await Taro.reLaunch({
146 + url: '/pages/Welcome/index'
147 + })
148 + }
179 } catch (finalError) { 149 } catch (finalError) {
180 console.error('最终降级方案也失败了:', finalError) 150 console.error('最终降级方案也失败了:', finalError)
181 - Taro.showToast({
182 - title: '系统异常,请重新打开小程序',
183 - icon: 'none',
184 - duration: 3000
185 - })
186 } 151 }
187 } 152 }
188 } 153 }
...@@ -235,3 +200,139 @@ export const addShareFlag = (path) => { ...@@ -235,3 +200,139 @@ export const addShareFlag = (path) => {
235 const separator = path.includes('?') ? '&' : '?' 200 const separator = path.includes('?') ? '&' : '?'
236 return `${path}${separator}from_share=1` 201 return `${path}${separator}from_share=1`
237 } 202 }
203 +
204 +/**
205 + * 静默授权处理函数
206 + * 在后台处理授权,不跳转页面,避免用户感知
207 + * @param {Function} onSuccess - 授权成功回调
208 + * @param {Function} onError - 授权失败回调
209 + * @returns {Promise} 授权结果
210 + */
211 +export const silentAuth = async (onSuccess, onError) => {
212 + return new Promise((resolve, reject) => {
213 + // 检查是否已经授权
214 + if (!needAuth()) {
215 + // 已经授权,直接返回成功
216 + if (onSuccess) {
217 + onSuccess({ code: 1, msg: '已授权' })
218 + }
219 + resolve({ code: 1, msg: '已授权' })
220 + return
221 + }
222 +
223 + // 显示loading提示
224 + Taro.showLoading({
225 + title: '加载中...',
226 + mask: true
227 + })
228 +
229 + // 调用微信登录
230 + Taro.login({
231 + success: function (res) {
232 + if (res.code) {
233 + // 构建请求数据
234 + const requestData = {
235 + code: res.code,
236 + }
237 +
238 + // 测试环境下传递openid,正式环境不传递
239 + if (process.env.NODE_ENV === 'development') {
240 + requestData.openid = 'h-008';
241 + // requestData.openid = 'h-009';
242 + // requestData.openid = 'h-010';
243 + // requestData.openid = 'h-011';
244 + // requestData.openid = 'h-012';
245 + // requestData.openid = 'h-013';
246 + // requestData.openid = 'oWbdFvkD5VtloC50wSNR9IWiU2q8';
247 + // requestData.openid = 'oex8h5QZnZJto3ttvO6swSvylAQo';
248 + }
249 +
250 + // 发起授权请求 - 使用request.post保持与手动授权一致
251 + request.post('/srv/?a=openid', requestData)
252 + .then(response => {
253 + Taro.hideLoading()
254 +
255 + if (response.data.code) {
256 + const cookie = response.cookies && response.cookies[0]
257 + if (cookie) {
258 + // 保存sessionid到本地存储
259 + wx.setStorageSync("sessionid", cookie)
260 +
261 + // 更新request默认headers
262 + request.defaults.headers.cookie = cookie
263 +
264 + // 静默授权成功
265 +
266 + // 执行成功回调
267 + if (onSuccess) {
268 + onSuccess(response.data)
269 + }
270 +
271 + resolve(response.data)
272 + } else {
273 + console.error('授权响应中没有cookie信息')
274 +
275 + if (onError) {
276 + onError('授权失败:没有获取到有效的会话信息')
277 + }
278 +
279 + reject(new Error('授权失败:没有获取到有效的会话信息'))
280 + }
281 + } else {
282 + console.error('静默授权失败:', response.data.msg)
283 +
284 + if (onError) {
285 + onError(response.data.msg || '授权失败')
286 + }
287 +
288 + reject(new Error(response.data.msg || '授权失败'))
289 + }
290 + })
291 + .catch(error => {
292 + console.error('静默授权请求失败:', error)
293 + Taro.hideLoading()
294 +
295 + if (onError) {
296 + onError('网络请求失败,请稍后重试')
297 + }
298 +
299 + reject(new Error('授权失败,请稍后重试'))
300 + })
301 + } else {
302 + console.error('微信登录失败:', res.errMsg)
303 + Taro.hideLoading()
304 +
305 + if (onError) {
306 + onError('微信登录失败:' + res.errMsg)
307 + }
308 +
309 + reject(new Error('微信登录失败:' + res.errMsg))
310 + }
311 + },
312 + fail: (error) => {
313 + console.error('调用微信登录失败:', error)
314 + Taro.hideLoading()
315 +
316 + if (onError) {
317 + onError('调用微信登录失败')
318 + }
319 +
320 + reject(error)
321 + }
322 + })
323 + })
324 +}
325 +
326 +/**
327 + * 检查是否需要授权
328 + * @returns {boolean} 是否需要授权
329 + */
330 +export const needAuth = () => {
331 + try {
332 + const sessionid = wx.getStorageSync("sessionid")
333 + return !sessionid || sessionid === ''
334 + } catch (error) {
335 + console.error('检查授权状态失败:', error)
336 + return true
337 + }
338 +}
......
1 /* 1 /*
2 * @Date: 2022-09-19 14:11:06 2 * @Date: 2022-09-19 14:11:06
3 * @LastEditors: hookehuyr hookehuyr@gmail.com 3 * @LastEditors: hookehuyr hookehuyr@gmail.com
4 - * @LastEditTime: 2025-09-04 15:33:42 4 + * @LastEditTime: 2025-09-12 10:44:34
5 * @FilePath: /lls_program/src/utils/request.js 5 * @FilePath: /lls_program/src/utils/request.js
6 * @Description: 简单axios封装,后续按实际处理 6 * @Description: 简单axios封装,后续按实际处理
7 */ 7 */
8 // import axios from 'axios' 8 // import axios from 'axios'
9 import axios from 'axios-miniprogram'; 9 import axios from 'axios-miniprogram';
10 // // import qs from 'Qs' 10 // // import qs from 'Qs'
11 -import { navigateToAuth } from '@/utils/authRedirect' 11 +import { silentAuth } from '@/utils/authRedirect'
12 12
13 // import { ProgressStart, ProgressEnd } from '@/components/axios-progress/progress'; 13 // import { ProgressStart, ProgressEnd } from '@/components/axios-progress/progress';
14 // import store from '@/store' 14 // import store from '@/store'
...@@ -147,18 +147,28 @@ service.interceptors.response.use( ...@@ -147,18 +147,28 @@ service.interceptors.response.use(
147 147
148 /** 148 /**
149 * 处理401未授权状态 149 * 处理401未授权状态
150 - * 清除本地sessionid并跳转到登录页 150 + * 清除本地sessionid并使用静默授权
151 */ 151 */
152 if (response.data.code === 401) { 152 if (response.data.code === 401) {
153 // 清除无效的sessionid 153 // 清除无效的sessionid
154 clearSessionId(); 154 clearSessionId();
155 - /** 155 +
156 - * 未授权跳转登录页 156 + // 使用静默授权处理,避免页面跳转
157 - * 授权完成后 返回当前页面 157 + return silentAuth(
158 - */ 158 + () => {
159 - setTimeout(() => { 159 + // 授权成功后重新发起原始请求
160 - navigateToAuth(); 160 + const originalRequest = response.config;
161 - }, 1000); 161 + return service.request(originalRequest);
162 + },
163 + (error) => {
164 + // 静默授权失败,直接返回错误,不跳转页面
165 + console.error('静默授权失败:', error);
166 + return Promise.reject(new Error('授权失败,请稍后重试'));
167 + }
168 + ).catch(() => {
169 + // 如果静默授权完全失败,直接返回错误,不跳转页面
170 + return Promise.reject(new Error('授权失败,请稍后重试'));
171 + });
162 } 172 }
163 return response 173 return response
164 }, 174 },
......