零、写在前面
免责声明:本文仅用于技术研究与课堂自动化测试示范,不鼓励也不支持任何违反平台服务协议、侵犯教师及学校权益的行为。切勿在真实生产账号、正式教学场景下使用本文脚本,否则一切后果自负。
一、learnCourseViewModel.js分析
该模块的主要保存逻辑:
- 节级别记录保存(Section Record):每学完一个节,保存一次节的学习记录,包括下属所有页面的完成状态、答题状态等。
- 页级别学习状态收集:每一页的记录(如观看视频、做题、语音录入)由页面组件自行维护,但最终集中在节级数据中上传保存。
- 定时保存 + 离开保存 + 页面切换保存:确保在意外退出、断网、跳转等情况下最大程度保留进度。
- 支持三方平台同步保存:为 LMS 或其他学习平台提供回调。
1.1、保存学习记录的核心函数
section.createRecord(force, status, chapterId, successCallback, failCallback, isLeave)
参数含义:
| 参数 | 含义 |
|---|---|
force | 是否强制保存(true 表示即使没有变更也强制提交) |
status | 是否完成(0未完成,1已完成) |
chapterId | 章节 ID(用于汇总上传) |
successCallback | 保存成功后的回调函数 |
failCallback | 保存失败的回调函数 |
isLeave | 是否是在离开页面时调用(控制是否提示) |
用法举例:
这行代码的意思是:保存当前节的学习记录,标记为未完成,触发记录上传。
1.2、触发保存的时机(非常关键)
1. 自动定时保存(每5分钟)
2. 离开页面时保存
3. 页面跳转时保存
在 selectPage 函数中,如果切换节,先保存当前节记录再跳转:
4. 返回目录页、退出学习时保存
5. 用户手动点击保存按钮
1.3、节学习完成自动保存逻辑
有一段很巧妙的定时监听器:
这说明:系统每5秒扫描当前节下的所有页面,一旦发现都学完,自动保存记录为“完成状态”。
1.4、与第三方平台的同步机制
1. URL 参数控制
支持以下参数触发同步保存:
jtoken:用于标识第三方身份令牌trdCourseId、classId、callbackUrl
2. 调用同步接口
1.5、保存失败恢复机制
系统会记录保存失败的节记录至 localStorage.failureRecord 中:
1.6、容错机制与状态判断
在保存前后都大量使用以下判断防止重复或错误触发:
isPreviewMode,isExpiredMode→ 是否为预览模式或已过期(跳过保存)section.isRecordLoaded()→ 节的学习记录是否已加载page.hasStarted,page.questionIncomplete→ 判断是否完成题目或语音
1.7、保存请求发起点分析
关键调用:
这是保存学习记录的统一入口,而 Section 对象是从:
模块加载的,也就是说它的 .createRecord() 方法就是发送保存请求的核心方法。
请求头信息
所有的 AJAX 请求在 $.ajaxSetup 中已统一配置:
保存的 URL 路径
从第三方同步接口路径可以看到 API 主机名配置为:
而自己的学习记录保存接口可能是:
或类似:
这些路径可能在
model/Section.js模块中定义。
二、Section.js分析
根据 Section.js 文件,“保存学习记录”时提交给后端的数据在通过 CryptoJS 加密 后通过 AJAX POST 请求发出,接口路径为:
2.1、请求数据结构
这是 ItemStudyRecordUpdateDTO 最终被提交的数据对象:
⚠️ 该结构会在请求前用 DES 加密处理:
CodeBlock Loading...
2.2、构建过程简析
页面循环构建
pageStudyRecordDTOList:CodeBlock Loading...每页内再提取题目记录:
CodeBlock Loading...视频记录细化结构:
CodeBlock Loading...口语题记录结构:
CodeBlock Loading...
2.3、失败处理与本地缓存
当请求失败时,将调用:
保存结构如下:
2.4、加密与解密
在 Section.js 中有非常关键的这一段:
加密算法参数如下:
| 参数 | 值 |
|---|---|
| 算法 | DES(对称加密) |
| 密钥 | "12345678"(固定字符串) |
| 编码 | Utf8.parse(...) |
| 模式 | ECB(电子密码本模式) |
| 填充 | Pkcs7(常见填充方式) |
加密结果
该方法生成的加密字符串是 Base64 编码过的密文。它作为 POST 请求的 body 被发送出去。
三、源码逆向要点
| 关键点 | 细节 |
|---|---|
| 学习记录结构 | itemid、autoSave、complete、pageStudyRecordDTOList … |
| 加密实现 | CryptoJS.DES.encrypt(JSON, key="12345678", ECB, Pkcs7) |
| 接口 | POST /api/yws/api/personal/sync?courseType=4&platform=PC |
| 目录 API | /api/course/stu/{courseId}/directory?classId=… |
| Chapter → WholePage | /api/wholepage/chapter/stu/{nodeId} |
| 题目答案 API | /api/questionAnswer/{questionId}?parentId={parentId} |
→ 前端把所有页面数据整合后 一次性 提交节级学习记录,只要字段对,后端就写库。
→ DES 密钥硬编码、ECB 模式 ⇒ 可完全复现加密。
四、脚本整体流程图
五、关键代码片段解析
4.1 DES-ECB 加密函数
与前端 CryptoJS 行为保持 100% 一致。
4.2 伪造单页学习记录
4.3 组装节级 DTO 并上传
六、脚本完整功能亮点
| 功能 | 说明 |
|---|---|
| 多课程交互 | 调用课程列表接口,支持用户选择 |
| 章节-小节智能遍历 | 自动识别视频页 / PPT页 / 仅答题页 |
| 答题正确率 100% | 题目答案接口返回 correctAnswerList,脚本依题型拼装 |
| 时间戳合理 | studyStartTime, startEndTimeList 与服务器时区一致 |
| 日志友好 | 上传成功 / 失败打印 itemid + pageid + 小节标题 |
| 异常回退 | 若接口 4xx/5xx,显示 res.text,方便排查 |
完整代码
七、风险点与避坑
- 后台风控
- 长时间批量高频提交容易触发
429 Too Many Requests;脚本已time.sleep(1)节流。
- 长时间批量高频提交容易触发
- 题库变动
- 若题目设置了「随机抽题」,旧 questionid 会失效 → 脚本取实时 id 即可。
- 密钥硬编码
- 官方如若更新密钥或改成 AES/CBC,脚本需同步调整。
- 账号封禁风险
- 海量秒学完 + 100 分极易被后台标记,务必仅在 测试班级 或 教辅白名单 环境使用。
八、一键运行指南
运行时交互流程:
- 复制浏览器
token→ 粘贴到脚本 - 选择课程序号
- 坐和收菜,观察日志
九、效果示例
全部节上传耗时 < 2 分钟,后台学习报告显示 已学完 / 100 分。
十、结语
通过阅读前端源码,我们发现 弱对称加密 + 前端硬编码密钥 常见于低成本 LMS 平台。
这为测试人员提供了便利,也暴露了平台接口安全短板。
正道是:
- 后端应对每条记录做 有效时长校验 / 视频播放区间比对
- 采用 动态密钥协商 或 JWT + HMAC 验签
- 服务端保存 原始播放日志 做二次风控
希望本文思路能帮助 QA、教辅或课程开发者更快地 自动化验证 课程链路,同时也能给平台安全团队一些完善的参考。