diff --git a/微信授权流程设计文档.md b/微信授权流程设计文档.md deleted file mode 100644 index 84a0051..0000000 --- a/微信授权流程设计文档.md +++ /dev/null @@ -1,256 +0,0 @@ -# 微信 H5 授权流程重构 - 设计文档 - -## 📋 核心业务需求 - -在微信 H5 环境中,**整个应用在没有有效 token 时,都需要进行微信授权登录**。授权完成后,用户应该被重定向回他们尝试访问的原始页面。 - ---- - -## 🔄 流程设计 - -### **完整的授权流程图** - -``` -┌─────────────────────────────────────────────────────────────┐ -│ 用户在微信中访问任意页面(无 token) │ -└──────────────────┬──────────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────────────────────┐ -│ 路由守卫检测:微信 + 无 token │ -│ → 保存目标路由到 authStore.pendingRoute │ -│ → 生成微信授权 URL │ -│ → window.location.href = 授权 URL (不调用 next()) │ -└──────────────────┬──────────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────────────────────┐ -│ 跳转到微信授权页面 │ -│ https://open.weixin.qq.com/connect/oauth2/authorize?... │ -└──────────────────┬──────────────────────────────────────────┘ - │ - ┌─────────┴─────────┐ - │ 用户点击"同意" │ - └────────┬──────────┘ - │ - ▼ -┌─────────────────────────────────────────────────────────────┐ -│ 微信回调:redirectUri?code=xxx&state=yyy │ -│ 浏览器重新加载应用,URL 中包含 code/state 参数 │ -└──────────────────┬──────────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────────────────────┐ -│ App.vue onMounted 检测到 code + state 参数 │ -│ → 调用 handleWeixinAuthCallback(code) │ -└──────────────────┬──────────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────────────────────┐ -│ 1. 调用后端接口 /user/wxh5Auth 交换 token │ -│ 2. 保存 token 到 localStorage │ -│ 3. 清理 URL 中的 code/state 参数 │ -│ 4. 获取用户信息 │ -│ 5. 标记授权完成 authStore.completeWeixinAuth() │ -└──────────────────┬──────────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────────────────────┐ -│ 获取 pendingRoute,重定向回原始页面 │ -│ router.replace(pendingRoute) │ -│ 如果没有 pendingRoute,重定向到首页 "/" │ -└──────────────────┬──────────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────────────────────┐ -│ ✅ 授权完成,用户看到原始页面 │ -└─────────────────────────────────────────────────────────────┘ -``` - ---- - -## 📁 关键文件修改 - -### **1. `src/router/index.js` - 路由守卫** - -**关键变化:** -- 检测到 **微信 + 无 token** 的情况 -- **直接发起授权**(调用 `window.location.href`) -- **不调用 `next()`**,完全停止导航 -- 授权 URL 中的 redirectUri 指向当前页面(清理旧的 code/state 参数) - -**代码片段:** -```javascript -if (isWeChat.value && !isAuthenticated && !isTokenExpired) { - // 保存目标路由 - authStore.startWeixinAuth(to); - - // 生成授权 URL - const appId = import.meta.env.VITE_WECHAT_APP_ID; - const url = new URL(window.location.href); - const params = new URLSearchParams(url.search); - params.delete("code"); - params.delete("state"); - const cleanUrl = `${url.origin}${url.pathname}${params.toString() ? "?" + params.toString() : ""}`; - const redirectUri = encodeURIComponent(cleanUrl); - const weixinAuthUrl = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${appId}&redirect_uri=${redirectUri}&response_type=code&scope=snsapi_base&state=snsapi_base#wechat_redirect`; - - // 直接跳转,不调用 next() - window.location.href = weixinAuthUrl; - return; -} -``` - -### **2. `src/App.vue` - 授权回调处理** - -**关键职责:** -1. **检测回调**:读取 URL 中的 code/state 参数 -2. **处理回调**:调用 `/user/wxh5Auth` 交换 token -3. **恢复导航**:使用保存的 pendingRoute 重定向用户 - -**核心函数:`handleWeixinAuthCallback(code)`** -```javascript -const handleWeixinAuthCallback = async (code) => { - // 1. 交换 token - const { data, error } = await useApiFetch("/user/wxh5Auth").post({ code }).json(); - - // 2. 保存 token - localStorage.setItem("token", data.value.data.accessToken); - localStorage.setItem("refreshAfter", data.value.data.refreshAfter); - localStorage.setItem("accessExpire", data.value.data.accessExpire); - - // 3. 清理 URL - const url = new URL(window.location.href); - const params = new URLSearchParams(url.search); - params.delete("code"); - params.delete("state"); - window.history.replaceState({}, "", newUrl); - - // 4. 获取用户信息 - await userStore.fetchUserInfo(); - - // 5. 标记授权完成 - authStore.completeWeixinAuth(); - - // 6. 重定向回 pendingRoute - const pendingRoute = authStore.pendingRoute; - await router.replace(pendingRoute || "/"); -}; -``` - -### **3. `src/stores/authStore.js` - 授权状态管理** - -**关键方法:** - -| 方法 | 作用 | -|------|------| -| `startWeixinAuth(targetRoute)` | 开始授权,保存目标路由 | -| `completeWeixinAuth()` | 标记授权完成 | -| `clearPendingRoute()` | 清除待处理路由 | -| `resetAuthState()` | 重置所有授权状态 | -| `restoreFromStorage()` | 页面刷新后恢复状态 | - ---- - -## 🎯 核心特性 - -### **1. 同步流程控制** -- ❌ **不使用** `watch` 监听 state 的变化 -- ❌ **不使用** 异步的 route guard + next() 来触发授权 -- ✅ **直接** 在 route guard 中调用 `window.location.href` 发起授权 - -**为什么?** -- Watch 是异步的,可能被路由完成打断 -- next() 后可能路由已经切换,导致授权流程混乱 -- 直接跳转 URL 更加可靠和同步 - -### **2. 状态持久化** -- 授权开始时保存到 localStorage -- 页面刷新时自动恢复状态 -- 防止超时(30秒)导致的无限授权循环 - -### **3. URL 参数清理** -- 每次发起授权前,都清理 URL 中旧的 code/state 参数 -- 防止参数被重复编码进 redirectUri -- 微信回调时才注入新的 code/state - -### **4. 目标页面恢复** -- 路由守卫保存用户尝试访问的页面 -- 授权完成后自动重定向到该页面 -- 用户无感知的完整授权体验 - ---- - -## 🔐 场景处理 - -### **场景 1:无 token + 访问开放页面** -``` -用户访问 "/" → 微信检测无 token → 发起授权 → 授权完成后回到首页 -``` - -### **场景 2:无 token + 访问推广链接** -``` -用户访问 "/agent/promotionInquire/abc123" - → 微信检测无 token - → 保存 pendingRoute = "/agent/promotionInquire/abc123" - → 发起授权 - → 授权完成后回到 "/agent/promotionInquire/abc123" -``` - -### **场景 3:有效 token + 访问任意页面** -``` -用户有 token 且未过期 → 路由守卫放行 → 直接加载页面,无授权 -``` - -### **场景 4:Token 过期 + 访问需登录页面** -``` -用户在非微信环境 → 检测到 token 过期 → 跳转登录页面 -``` - -### **场景 5:授权超时** -``` -用户授权流程中,30 秒内未完成 → 自动重置状态 → 页面刷新时重新授权 -``` - ---- - -## 🐛 常见问题排查 - -### **Q1:为什么授权后没有跳到原页面?** -**A:** 检查 authStore 中是否有 pendingRoute -```javascript -console.log('pendingRoute:', authStore.pendingRoute); -``` - -### **Q2:为什么一直在授权页面循环?** -**A:** 可能是授权回调处理失败。检查: -1. 后端 `/user/wxh5Auth` 接口是否正确返回 token -2. Token 是否正确保存到 localStorage -3. URL 中的 code/state 是否正确清理 - -### **Q3:为什么刷新页面后授权状态丢失?** -**A:** authStore.restoreFromStorage() 应该在 onMounted 中被调用。检查: -```javascript -authStore.restoreFromStorage(); // 这行很重要! -``` - -### **Q4:在 PC 上测试,为什么无法触发授权?** -**A:** 授权只在微信环境中触发(isWeChat.value === true)。在 PC 上: -- 访问需登录的页面 → 跳转登录页 -- 访问开放页面 → 正常加载 - ---- - -## 📝 总结 - -这个重构的核心思想是: -1. **路由守卫 = 决策者**:检测到需要授权就立即发起 -2. **App.vue = 处理器**:处理授权回调和状态恢复 -3. **AuthStore = 状态管理**:保存和恢复授权相关的状态 -4. **同步流程 = 高可靠性**:避免异步竞态条件 - -通过这个设计,微信授权流程变得: -- ✅ 清晰易懂 -- ✅ 可靠稳定 -- ✅ 易于测试和调试 -- ✅ 完整的用户体验 diff --git a/微信授权重定向问题分析与修复.md b/微信授权重定向问题分析与修复.md deleted file mode 100644 index 8227821..0000000 --- a/微信授权重定向问题分析与修复.md +++ /dev/null @@ -1,121 +0,0 @@ -# 微信授权重定向问题分析与修复 - -## 问题描述 -微信授权回调成功后,虽然 `pendingRoute` 有正确的值,回调成功也能跳转到应该到达的页面(如 `promotionInquire`),但在加载该页面后,又会自动跳转到首页。 - -## 根本原因分析 - -### 问题 1: pendingRoute 清除时序错误 ❌ -**位置**: `src/App.vue` 第 69-72 行 - -**原始代码**: -```javascript -if (pendingRoute) { - authStore.clearPendingRoute(); // ❌ 先清除 - await router.replace(pendingRoute); // 用已清空的 null 值跳转 -} -``` - -**问题**: -- `clearPendingRoute()` 将 `authStore.pendingRoute` 设为 `null` -- 之后 `router.replace(pendingRoute)` 使用的是已清空的 `null` 值 -- 导致实际跳转到 `undefined` 而非目标路由 - -**修复**: 先跳转再清除 ✅ - -### 问题 2: 路由守卫重复授权防护缺失 ❌ -**位置**: `src/router/index.js` 第 428-444 行 - -**问题**: -- 当用户在授权完成后,路由守卫再次执行时 -- 如果 `isWeChat.value && !isAuthenticated` 条件满足 -- 会重新触发授权流程,导致页面跳转 - -**原因**: -- `authStore.isWeixinAuthing` 和 `authStore.weixinAuthComplete` 状态未被路由守卫检查 -- 虽然 token 已保存,但守卫仍可能因某些时序问题再次触发授权 - -**修复**: 在路由守卫中增加授权状态检查 ✅ - -### 问题 3: localStorage 同步问题 -**位置**: `src/stores/authStore.js` - -**问题**: -- 清除 `pendingRoute` 时,可能只清除内存状态,localStorage 可能残留数据 -- 页面刷新时恢复的数据可能不完整 - -**修复**: 确保内存和 localStorage 同步清除 ✅ - -## 修复清单 - -### ✅ 修复 1: App.vue 中的时序问题 -```javascript -if (pendingRoute) { - // ✅ 先跳转 - await router.replace(pendingRoute); - // ✅ 再清除 - authStore.clearPendingRoute(); -} -``` - -### ✅ 修复 2: 路由守卫的防护检查 -在路由守卫中增加: -```javascript -if (isWeChat.value && !isAuthenticated && !isTokenExpired) { - // ✨ 新增:检查是否正在授权或已完成授权 - if (authStore.isWeixinAuthing || authStore.weixinAuthComplete) { - console.warn("⚠️ WeChat auth already in progress or completed"); - NProgress.done(); - next(); - return; - } - // ... 继续原有逻辑 -} -``` - -### ✅ 修复 3: authStore 中的日志和同步 -增强 `clearPendingRoute()` 和 `restoreFromStorage()` 方法的日志记录和错误处理。 - -### ✅ 修复 4: PromotionInquire.vue 中的延迟处理 -添加延迟以确保页面完全加载后再进行任何路由跳转: -```javascript -function isFinishPayment() { - const query = new URLSearchParams(window.location.search); - let orderNo = query.get("out_trade_no"); - if (orderNo) { - // ✨ 延迟 100ms 确保页面加载完成 - setTimeout(() => { - router.push({ path: "/report", query: { orderNo } }); - }, 100); - } -} -``` - -## 调试建议 - -### 观察日志的关键点: -1. **微信授权开始**: 看是否出现 "Triggering WeChat auth from route guard" -2. **Token 保存**: 看是否出现 "✅ Token saved successfully" -3. **用户信息加载**: 看 "✅ User info fetched" 是否成功 -4. **pendingRoute 获取**: 看 "🎯 pendingRoute:" 后面的值 -5. **导航执行**: 看 "🚀 Navigating to pendingRoute:" 和 "✅ Navigated to pendingRoute" -6. **是否重复授权**: 看是否出现 "⚠️ WeChat auth already in progress" - -### 测试步骤: -1. 在微信中打开推广链接: `https://xxx.com/agent/promotionInquire/abc123` -2. 观察控制台日志,确保看到上述所有成功日志 -3. 验证最终页面是 `promotionInquire` 而非首页 - -## 修复文件列表 -- ✅ `src/App.vue` - 修复 pendingRoute 时序问题 -- ✅ `src/router/index.js` - 增强路由守卫防护 -- ✅ `src/stores/authStore.js` - 改进日志和同步 -- ✅ `src/views/PromotionInquire.vue` - 添加延迟处理 - -## 总结 -这个问题是由多个时序和状态管理问题联合造成的: -1. pendingRoute 被过早清除导致跳转失败 -2. 路由守卫缺少防护措施可能重复授权 -3. 状态同步不完整可能导致恢复失败 - -通过修复这些问题,应该能够确保微信授权后正确重定向到目标页面。