# `atu-prod` 分支前端加固迁移执行手册 ## 目的 这份文档用于在后续切换到 `atu-prod` 分支后,按固定步骤复用当前分支已经落地的前端防扒取加固方案。 使用方式很简单: - 当你切到 `atu-prod` 后,只要说明“按 `atu-prod-hardening-playbook.zh-CN.md` 执行” - 我就按这份文档中的顺序检查、迁移、修复和验证 这份文档不是泛泛的说明,而是面向实际执行的作业手册。 ## 适用前提 - `atu-prod` 分支与当前分支在业务功能上大体一致 - 主要差异集中在样式、布局、局部结构命名 - 活动页和榜单页的核心交互、接口、资源使用方式基本相同 如果后续发现 `atu-prod` 在页面结构上差异很大,这份手册仍然能作为基线,但执行时要允许局部调整。 ## 迁移目标 切换到 `atu-prod` 后,需要尽量恢复当前分支已经完成的这些能力: 1. 受保护页面仅允许在 App 环境下访问 2. 运行时基础防调试、防复制、防拖拽和动态水印 3. `Activities / Ranking` 图片地址保护与运行时解析 4. `background-image` 相关真实图链泄露点收口 5. 构建产物哈希化和可读性降低 6. 生产相关模式日志清理与保留日志开关 7. `v-show` 到 `v-if` 的主内容延迟挂载改造 8. `BackgroundLayer` 的 `useCanvas` 背景合成方案 9. 明显语义类名收口 10. 已发现问题页的修复和清理 ## 执行顺序 后续在 `atu-prod` 分支执行时,按下面顺序做,不要跳步。 ### 第 1 步:先对照基础能力文件 优先检查这些全局能力文件是否已经存在、是否需要同步: - [src/utils/routeGuard.js](/D:/programs/likei-h5/src/utils/routeGuard.js) - [src/utils/http.js](/D:/programs/likei-h5/src/utils/http.js) - [src/config/security.js](/D:/programs/likei-h5/src/config/security.js) - [src/utils/runtimeSecurity.js](/D:/programs/likei-h5/src/utils/runtimeSecurity.js) - [src/main.js](/D:/programs/likei-h5/src/main.js) - [src/App.vue](/D:/programs/likei-h5/src/App.vue) - [src/config/imagePaths.js](/D:/programs/likei-h5/src/config/imagePaths.js) - [src/utils/protectedAssets.js](/D:/programs/likei-h5/src/utils/protectedAssets.js) - [src/utils/protectedAssetRuntime.js](/D:/programs/likei-h5/src/utils/protectedAssetRuntime.js) - [src/directives/smartImage.js](/D:/programs/likei-h5/src/directives/smartImage.js) - [src/utils/image/imageCacheManager.js](/D:/programs/likei-h5/src/utils/image/imageCacheManager.js) - [src/components/BackgroundLayer.vue](/D:/programs/likei-h5/src/components/BackgroundLayer.vue) - [vite.config.js](/D:/programs/likei-h5/vite.config.js) - [package.json](/D:/programs/likei-h5/package.json) - [scripts/build-with-version.js](/D:/programs/likei-h5/scripts/build-with-version.js) 这一步的目标是先把“全局基础设施”对齐,再进页面级迁移。 ### 第 2 步:扫描 `Activities / Ranking` 页面 重点扫描这两个目录: - [src/views/Activities](/D:/programs/likei-h5/src/views/Activities) - [src/views/Ranking](/D:/programs/likei-h5/src/views/Ranking) 执行时重点看四类点: 1. 是否仍有大量直接 `` 2. 是否仍有 `background-image: url(...)` 3. 是否存在 `v-show="!isLoading"` 控制主内容 4. 是否存在明显语义类名、明显业务含义 chunk 入口、测试日志 ### 第 3 步:优先迁移高风险页面 后续在 `atu-prod` 中优先处理这些已经确认过风险较高、且当前分支已加固的页面: - [src/views/Activities/LesserBairam/index.vue](/D:/programs/likei-h5/src/views/Activities/LesserBairam/index.vue) - [src/views/Activities/LoginReward/index.vue](/D:/programs/likei-h5/src/views/Activities/LoginReward/index.vue) - [src/views/Activities/LuckyDollars/Season3/index.vue](/D:/programs/likei-h5/src/views/Activities/LuckyDollars/Season3/index.vue) - [src/views/Activities/LuckyDollars/Season4/index.vue](/D:/programs/likei-h5/src/views/Activities/LuckyDollars/Season4/index.vue) - [src/views/Activities/SpringFestival/index.vue](/D:/programs/likei-h5/src/views/Activities/SpringFestival/index.vue) - [src/views/Activities/SpringFestival/Ranking.vue](/D:/programs/likei-h5/src/views/Activities/SpringFestival/Ranking.vue) - [src/views/Activities/SpringFestival/Task.vue](/D:/programs/likei-h5/src/views/Activities/SpringFestival/Task.vue) - [src/views/Ranking/Couple/index.vue](/D:/programs/likei-h5/src/views/Ranking/Couple/index.vue) - [src/views/Ranking/Couple/Ranking.vue](/D:/programs/likei-h5/src/views/Ranking/Couple/Ranking.vue) - [src/views/Ranking/GamesKing/index.vue](/D:/programs/likei-h5/src/views/Ranking/GamesKing/index.vue) - [src/views/Ranking/KingAndQueen/TopList.vue](/D:/programs/likei-h5/src/views/Ranking/KingAndQueen/TopList.vue) - [src/views/Ranking/Overall/Ranking.vue](/D:/programs/likei-h5/src/views/Ranking/Overall/Ranking.vue) - [src/views/Ranking/WeeklyStar/WeeklyStar.vue](/D:/programs/likei-h5/src/views/Ranking/WeeklyStar/WeeklyStar.vue) ## 页面级迁移检查清单 后续每处理一个页面,都按这份清单检查。 ### A. 图片链路 - 是否统一通过 `getPngUrl()` / `getWebpUrl()` 走受保护地址 - 是否尽量使用 `v-smart-img` - 是否存在直接暴露真实 OSS 地址的模板位点 - 是否存在必须保留的 CSS 背景图场景 ### B. 背景层 - 页面背景是否可改成 [BackgroundLayer.vue](/D:/programs/likei-h5/src/components/BackgroundLayer.vue) - 如果是多层纯装饰背景,是否开启 `:useCanvas="true"` - 如果只是普通内容图,不强制改成 canvas 说明: - `useCanvas` 不应一刀切设成默认值 `true` - 它只针对敏感背景层启用,避免把普通页面也全部切到更重的渲染路径 ### C. 主内容挂载时机 - 页面是否仍使用 `v-show="!isLoading"` 控制主内容 - 如果页面存在预加载和监听时序问题,优先改成 `v-if="!isLoading"` - 如果页面依赖 `IntersectionObserver`,应在 `await nextTick()` 后再注册 - 如果预加载可能失败,确保在 `finally` 中放行主内容,避免整页空白 ### D. 语义暴露 - 是否有过于直白的局部类名 - 是否有业务含义明显的本地变量名或测试痕迹 - 是否有无用日志、调试文案、历史残留注释 ### E. 组件生命周期 - `IntersectionObserver` 是否在卸载时 `disconnect()` - 定时器是否在卸载时清理 - 残留监听代码是否已经失效却未移除 ## 已知问题与处理记录 后续迁到 `atu-prod` 时,优先留意这些已经踩过的坑。 ### 1. `v-show` 会让主内容 DOM 提前暴露 现象: - 页面还在预加载时,DOM 已经被挂载 - 容易被直接从 `Elements` 面板里抄结构 - 某些触底监听也会因为节点时序问题失灵 处理方式: - 将主内容改为 `v-if="!isLoading"` - 对依赖监听的组件,在 `nextTick()` 后注册观察器 - 在 `finally` 中放行主内容 涉及页面: - `LoginReward` - `LuckyDollars/Season4` - `SpringFestival` - `Ranking/Couple` - `Ranking/Overall` ### 2. `IntersectionObserver` 挂载时机不稳 现象: - 监听节点存在,但绑定太早 - 页面结构还没稳定时就执行 `observe()` - 页面切走后观察器残留 处理方式: - `await nextTick()` - 再 `observer.observe(...)` - `onUnmounted` 时 `disconnect()` 重点页面: - [src/views/Activities/SpringFestival/Ranking.vue](/D:/programs/likei-h5/src/views/Activities/SpringFestival/Ranking.vue) ### 3. `Ranking/Couple/Ranking.vue` 曾有残留无效加载监听代码 现象: - 组件里保留了已经取消的加载模块逻辑 - 相关 `ref` 节点已不存在,但监听代码还在 处理方式: - 清理失效的 `IntersectionObserver` 逻辑 - 删除无效加载模块相关变量 - 保留当前真实在用的数据展示逻辑 ### 4. `Ranking/Couple/Ranking.vue` 曾出现乱码注释 现象: - 早期编辑和编码处理后,中文注释出现乱码 处理方式: - 清理乱码注释 - 统一改为中文注释 - 保持逻辑不变,只修复可读性 ### 5. `BackgroundLayer` 为什么不是默认 `useCanvas=true` 结论: - 因为它是公共组件 - 默认全开会把所有背景层都切到 canvas 路径 - 会增加普通页面的图片加载、重绘和兼容风险 当前策略: - 只在高风险的 `Activities / Ranking` 页面显式传入 `:useCanvas="true"` ### 6. `toCssBackgroundImage()` 的作用 结论: - 它不是加密层 - 它是把 `likei-protected:` 地址还原成 CSS 可用的 `url(...)` - 主要用于必须保留 `background-image` 的点位 相关文件: - [src/utils/protectedAssets.js](/D:/programs/likei-h5/src/utils/protectedAssets.js) ## 构建与验证步骤 ### 基础构建 执行: ```bash npm run build ``` 预期: - 构建成功 - 保留已知提示,但不新增本轮改动引起的报错 ### 生产态验证 执行: ```bash npm run dev:prod ``` 或: ```bash npm run build npm run preview ``` 验证点: - 非 App 环境访问受保护页会被拦截 - 公共页可正常打开 - `Activities / Ranking` 页面的图片仍能显示 - `BackgroundLayer` 的 canvas 背景仍能正常渲染 - 触底加载和倒计时逻辑不受影响 ### 日志构建验证 执行: ```bash npm run build:prod npm run build:prod:logs npm run build:atu-prod npm run build:atu-prod:logs ``` 验证点: - 默认生产相关包清理 `console.log`、`console.debug`、`console.info`、`debugger` - 带 `:logs` 的命令仍保留调试日志,便于 App 内定位问题 - Jenkins 使用原有命令不需要改发布流程 ## 建议的迁移方法 后续切到 `atu-prod` 后,建议按下面方式推进: 1. 先同步全局基础能力文件 2. 再扫描 `Activities / Ranking` 3. 先迁移高风险页面 4. 每迁移一页就做局部验证 5. 最后统一跑 `npm run build` 6. 再补文档同步 ## 后续文档同步方法 如果后续主分支继续更新了这些安全文档,想把最新 md 同步到当前分支,不需要再手工复制。 先决条件: - 主分支上的文档更新已经提交到 Git - 当前已经切换到目标分支,例如 `atu_prod` 执行命令: ```bash npm run docs:sync:security -- master ``` 如果你想从远端主分支同步,也可以用: ```bash npm run docs:sync:security -- origin/master ``` 这条命令会同步这些文件: - `docs/security/frontend-hardening.md` - `docs/security/frontend-hardening.zh-CN.md` - `docs/security/frontend-hardening.zh-CN.updates.md` - `docs/security/atu-prod-hardening-playbook.zh-CN.md` 对应脚本文件: - [scripts/sync-security-docs.js](/D:/programs/likei-h5/scripts/sync-security-docs.js) 注意: - 如果源分支里还没有提交这些 md,脚本会直接报错 - 所以推荐流程是“先在主分支提交文档,再切到目标分支执行同步命令” ## 文档同步要求 后续在 `atu-prod` 做迁移时,文档也要同步更新,至少包含: - 本次迁移到 `atu-prod` 的实际范围 - 哪些页面已迁移 - 哪些页面因结构差异暂未迁移 - 新遇到的问题和处理方式 - 最终验证结果 建议优先更新这些文档: - [docs/security/frontend-hardening.zh-CN.md](/D:/programs/likei-h5/docs/security/frontend-hardening.zh-CN.md) - [docs/security/frontend-hardening.zh-CN.updates.md](/D:/programs/likei-h5/docs/security/frontend-hardening.zh-CN.updates.md) - [docs/security/atu-prod-hardening-playbook.zh-CN.md](/D:/programs/likei-h5/docs/security/atu-prod-hardening-playbook.zh-CN.md) ## 后续对我下指令的推荐说法 后续你切到 `atu-prod` 分支后,可以直接这样说: - 按 `atu-prod-hardening-playbook.zh-CN.md` 执行一遍 - 先按手册同步全局加固能力,再扫描 `Activities / Ranking` - 按手册把高风险页面优先迁移,并同步更新文档 看到这类指令后,我默认会: - 先核对分支当前状态 - 再按这份文档逐项执行 - 遇到结构差异时在不偏离目标的前提下做适配 - 最后把实际执行结果写回文档