362 lines
12 KiB
Markdown
362 lines
12 KiB
Markdown
# `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. 是否仍有大量直接 `<img :src="getPngUrl(...)" />`
|
||
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`
|
||
- 按手册把高风险页面优先迁移,并同步更新文档
|
||
|
||
看到这类指令后,我默认会:
|
||
|
||
- 先核对分支当前状态
|
||
- 再按这份文档逐项执行
|
||
- 遇到结构差异时在不偏离目标的前提下做适配
|
||
- 最后把实际执行结果写回文档
|