6.0 KiB
6.0 KiB
方案:小程序跳转到 H5 页面携带 token
Summary
- 目标:在微信小程序内通过 web-view 打开指定 H5 地址时,让 H5 自动获得与小程序一致的登录态(token),并在 H5 内完成 Vuex/缓存写入与用户信息同步。
- 约束(来自现状与确认):打开方式为 web-view;当前不方便改后端接口;倾向直接携带 Bearer token。
- 推荐策略:优先把 token 放到 URL 的 fragment(
#之后)以避免出现在 HTTP 请求与 Referer/服务端日志里;H5 启动时从 fragment 解析 token,写入LOGIN_STATUS缓存并拉取用户信息,随后清理 URL 中的 token。
Current State Analysis
- 小程序打开 H5 的通用入口:
utils/util.js: JumpPath,当 url 包含http时在 MP/APP-PLUS 走 web-view 页面:- util.js
- web-view 页面接收
option.url并decodeURIComponent后赋给<web-view :src="url">:
- H5 端已有“从 URL query 自动登录”的机制,但目前只处理
remote_token:- H5
App.vue:if (!isLogin && option.query.hasOwnProperty('remote_token')) this.remoteRegister(...) - App.vue
remoteRegister调用GET /api/remote_register(noAuth)换取 token 或跳转 URL:
- H5
- token 使用方式:请求层统一在
utils/request.js注入 headerAuthori-zation: Bearer <token>: - 登录态落地:
store/modules/app.js: LOGIN将 token 写入Cache.set(LOGIN_STATUS, token, time);并可配合getUserInfo()写入 uid/userInfo:
Proposed Changes
1) 小程序侧:构造带 token 的 H5 URL(尽量放在 fragment)
- 修改文件:
utils/util.js - 修改点:在
JumpPath的url.indexOf("http") != -1分支(MP/APP-PLUS)中,在跳转到/pages/annex/web_view/index之前对目标 url 做“携带 token”的包装。 - 行为设计:
- 若用户未登录(
store.getters.isLogin为 false 或store.state.app.token为空),直接走现有登录流程(可复用libs/login.js: toLogin),不打开 H5。 - 若已登录:
- 优先把 token 附加在 hash 内:
https://h5.xxx.com/#/some/path?token=<encoded> - 若目标 url 不含
#,则退化为 query:https://h5.xxx.com/some/path?token=<encoded>(存在服务端日志/Referer 风险,属于“不得不”的兼容路径)。
- 优先把 token 附加在 hash 内:
- 在把 url 作为参数传给 web_view 页面时,对整段 url 做
encodeURIComponent,避免 token 中的特殊字符以及&被小程序路由二次解析。
- 若用户未登录(
- 预计新增/调整函数(放在 util.js 内部):
appendTokenToUrl(rawUrl, token): string(只做字符串处理,不引入新依赖)encodeForWebView(url): string(统一encodeURIComponent)
- 影响面:
- 任何通过
this.$util.JumpPath('https://...')打开的 H5 链接,都将自动带上 token(仅在 MP/APP-PLUS 生效)。
- 任何通过
2) web-view 容器页:强制 decode 前提下保证传参完整
- 修改文件:
pages/annex/web_view/index.vue - 修改点:保持现有
decodeURIComponent(option.url),但要求所有调用方都传入 encode 后的 url。 - 如果项目内存在“未 encode 直接拼接 url”的调用点(当前
utils/util.js即如此),统一改为 encode 后再传。
3) H5 侧:解析 token → 写入登录态 → 拉取用户信息 → 清理 URL
- 修改文件:
App.vue - 修改点:H5
onLaunch内(// #ifdef H5分支)补充 token 自动登录逻辑,优先级建议:- 若当前已登录(
this.$store.getters.isLogin为 true),不处理外部 token(避免覆盖用户主动登录态)。 - 仍保留现有
remote_token分支逻辑(兼容已有能力)。 - 新增
token分支逻辑:- 从
option.query.token读取(query 方案) - 若取不到,再从
location.hash里解析token(fragment 方案) - 拿到 token 后:
this.$store.commit("LOGIN", { token, time: 0 })(time=0 表示缓存不主动过期,后续由服务端 410xxx 兜底踢下线)- 调用
getUserInfo(),成功后SETUID/UPDATE_USERINFO - 最后用
history.replaceState把 URL 中的 token 移除(避免用户复制链接时携带 token)
- 从
- 若当前已登录(
- 需要补一个轻量解析方法(不引入库):
parseTokenFromHash(hash): string | '':兼容#/path?token=...和#/?token=...两种。
Assumptions & Decisions
- H5 地址可能是 uni-app 默认 hash 路由(
/#/);因此默认把 token 放在 fragment 内最安全。 - 不改后端时,“直接 token”不可避免有泄露风险;通过 fragment + 清理 URL 将风险降到相对可接受:
- fragment 不会出现在 HTTP 请求行与一般服务端 access log
- Referer 通常不包含 fragment
- token 仍可能在以下场景泄露:用户截屏、设备调试、前端错误上报采集
location.href等;因此上线前需确认前端埋点/监控不会上报完整 URL。
Verification
- 本地/测试环境:
- 小程序端登录后,通过任意入口打开一个 H5 链接(走
JumpPath):- web-view 打开的 H5 页面无需再次登录
- H5 内
store.getters.isLogin为 true,且请求头带Authori-zation: Bearer ... getUserInfo()可正常返回并写入 uid/userInfo
- 打开后检查 H5 当前地址:token 参数已被移除(history.replaceState 生效)
- 小程序端登录后,通过任意入口打开一个 H5 链接(走
- 回归:
- 未登录时打开 H5 链接:应先跳登录,不应打开带 token 的 H5
- H5 原有
remote_token登录不受影响