MyBlog

华医网登录状态保活机制分析

这次其实做了一次很典型的浏览器端黑盒逆向 + 故障定位。最后不仅找到了“为什么挂一会就掉登录”,还顺带把华医网课程页的大致运行机制摸清了。

1. 最初的问题

起点只是:

华医网页面挂一会就提示重新登录,到底是 Cookie 到期、Session 超时,还是浏览器后台导致的?

最开始有几种假设:

Cookie 固定过期
SSO token 固定过期
ASP.NET Session idle timeout
播放器心跳停止
Safari / Edge 后台节流
多设备挤掉登录

所以整个过程本质上是在不断缩小假设空间。


2. Cookie 层:先排除了几个伪线索

最先看到:

HWWAFSESID
HWWAFSESTIME

一开始容易把 HWWAFSESTIME 理解成登录时间。

后来确认这其实是华为云 WAF 的安全会话 Cookie:

HWWAFSESID
HWWAFSESTIME

属于 WAF / 防护 / 会话统计体系,不是华医网业务登录凭据。

因此排除:

HWWAFSESTIME ≠ 华医登录过期时间

随后真正值得关注的 Cookie 是:

ASP.NET_SessionId
cookie_user_ticket
sso_code
sso_token

而这些 Cookie 的过期类型全部是:

Session

也就是浏览器侧没有明确:

Expires
Max-Age

所以:

客户端无法直接通过 Cookie 看出服务器什么时候判登录失效。


3. sso_token 被确认是 UUID v4

进一步检查发现:

sso_token

不是 JWT,而是:

xxxxxxxx-xxxx-4xxx-xxxx-xxxxxxxxxxxx

标准 UUID v4 风格。

这意味着它更像:

浏览器持有一个随机 Session ID
        ↓
服务器根据这个 ID
去 Redis / DB / Session Store 查询登录状态

而不是 JWT:

token 自己包含 iat / exp / user 信息

因此从 sso_token 字符串本身也无法推导 TTL。

这一步把机制进一步缩成了:

客户端:
固定 UUID token

服务器:
保存真正的 session 状态与 TTL

4. LocalStorage 中的“5 秒心跳”其实是神策统计

接着在 LocalStorage 中发现:

sawebjssdkpageleave-...

里面有:

heartbeat_interval_time = 5000
time = 不断变化

一开始很像登录心跳。

后来判断出这是 Sensors Data / 神策分析 PageLeave 插件的数据。

它的职责更接近:

每 5 秒记录页面仍然活着
→ 统计页面停留时间
→ 页面隐藏 / 离开时上报行为分析

而不是:

每 5 秒续华医登录 Session

因此又排除了一个很大的干扰项:

神策 heartbeat ≠ 华医登录 heartbeat

这个过程很典型:看到“heartbeat”不要马上把它理解成业务保活。


5. Network 分析:把流量拆成了两套系统

这是整个逆向里最重要的一步。

观察课程播放时的 Network 后,发现请求大致分成两套。

第一套:POLYV / 保利威视频系统

例如:

prtas.videocc.net/v2/view

以及一些 hash 风格的资源请求:

9fbd5960596d...

实验发现:

播放视频
→ hash 请求不断出现

暂停视频
→ hash 请求停止

所以基本可以确认这些是:

视频分片 / 视频媒体资源

而:

prtas.videocc.net/v2/view

则更像播放器统计、播放日志、流量统计。

因此得出:

视频系统
= POLYV / videocc.net

第二套:华医网自己的业务系统

发现:

https://cme28.91huayi.com/ashx/getCourseWarePlayState.ashx

它返回:

{
  "errorCode": "1",
  "errMsg": "ok",
  "reValue": null,
  "reBody": null
}

显然不是视频资源,而是 ASP.NET 业务接口。

从名字判断属于:

获取课件播放状态

它即使视频暂停也会周期发送,所以更像:

课程状态轮询

而不是视频本身。


6. 真正找到核心接口:addCourseWareProcess

之后抓到了最关键的请求:

POST /ashx/addCourseWareProcess.ashx

正常时:

200 OK

响应:

{
  "code": 0,
  "msg": "ok",
  "body": null
}

登录失效后:

401 Unauthorized

响应:

{
  "code": 401,
  "message": "请重新登录",
  "body": null
}

这个接口名字本身已经很明确:

add CourseWare Process

基本就是:

提交 / 保存课件学习进度。

于是整个业务链开始清晰:

POLYV
负责:
视频播放
视频分片
播放器统计

华医网
负责:
登录
课程状态
学习进度
是否完成课程

这也解释了一个非常重要的现象:

华医登录已经失效
↓
POLYV 视频仍然可能继续播放
↓
用户表面上看“课程还在播放”
↓
但 addCourseWareProcess 已经 401
↓
学习进度实际上不再保存

也就是说:

“视频还在播放” ≠ “华医还在正常记学习进度”。

这是整个逆向里最重要的业务发现之一。


7. 401 时 Cookie 仍然存在

接下来又确认了一点:

401 请求发出时:

sso_token
sso_code
ASP.NET_SessionId
...

这些 Cookie 仍然有值并且被正常发送。

因此基本排除:

Safari / Edge 把 Cookie 删除了
Cookie 自己到期消失

真正发生的是:

浏览器:
“这是我的旧 token”

服务器:
“这个 token 我已经不认了”

→ 401

也就是说:

登录失效发生在服务端。

而不是客户端 Cookie 生命周期问题。


8. 一度怀疑固定 SSO TTL

由于:

sso_token

多次成功请求之间值完全不变,所以排除了:

每个请求都刷新成一个新 token

当时剩下两个主要假设:

A. SSO 有绝对过期时间
   登录 N 分钟后强制失效

B. 服务端存在 idle timeout
   长时间没有有效活动后失效

但真正的突破来自请求时间线。


9. 决定性的时间线异常

正常情况下:

addCourseWareProcess

大约:

每 5 分钟一次。

但某次抓包发现:

14:57
addCourseWareProcess → 200

之后长达约 70 分钟没有请求

16:07
addCourseWareProcess → 401

这时真正异常的地方已经不是:

为什么 16:07 过期?

而是:

为什么一个本应每 5 分钟发生一次的请求,整整 70 分钟没发?

正常情况下应该看到:

14:57
15:02
15:07
15:12
...
16:02

但这些全部消失了。

这一下把重点从:

华医服务端固定 TTL

转移到了:

浏览器后台生命周期

10. 最终定位:Edge 睡眠 / 节能标签页

于是检查 Edge 的:

Sleeping Tabs
Energy Saver
后台标签页冻结

Chromium / Edge 为了节省 CPU、内存和电量,会对长时间后台标签页:

Throttle
→ Freeze
→ Sleep

被冻结后:

setInterval
setTimeout
网页 JS 定时任务

都可能暂停。

于是完整故障链终于成立:

课程正常播放
↓
用户切走 / 页面长期后台
↓
Edge 节能策略将页面冻结 / 睡眠
↓
网页 JS 定时任务停止
↓
addCourseWareProcess 不再每 5 分钟发送
↓
华医服务器长时间没有收到有效业务活动
↓
服务端 Session / SSO 状态失效
↓
页面重新恢复运行
↓
addCourseWareProcess 再次发送
↓
旧 sso_token 仍然被浏览器带上
↓
服务器判定已失效
↓
401 请重新登录

这条链基本把所有观察现象都解释通了。


11. 最终实验验证

你最后做了实际对照:

让网页播放声音
+
把华医网从 Edge 节能 / 睡眠机制中排除
+
Windows 不睡眠

结果:

连续几个小时登录都没有再掉。

而且 addCourseWareProcess 能持续发送。

这相当于给整个假设做了一个很强的实验验证。

因此最终结论可以写成:

华医网本身并没有表现出一个很短的固定登录 TTL。之前的掉登录主要是由于 Edge 在后台对课程页执行节能 / 睡眠策略,导致网页 JS 定时业务请求停止。长时间没有有效业务请求后,服务端 Session/SSO 状态失效。将网站设置为“始终保持活动”并保持媒体播放后,addCourseWareProcess 能持续约每 5 分钟发送,登录状态可以保持数小时。


12. 最后得到的华医网页面模型

现在已经可以画出一个相当清晰的架构:

                 华医课程页面
                      │
        ┌─────────────┴─────────────┐
        │                           │
        ▼                           ▼
   POLYV 保利威                 华医业务后端
 videocc.net               cme28.91huayi.com
        │                           │
        │                           ├─ 登录 / SSO
        ├─ 视频资源                 │
        ├─ 视频分片                 ├─ getCourseWarePlayState
        ├─ 播放日志                 │     课程状态轮询
        └─ view统计                 │
                                    └─ addCourseWareProcess
                                          学习进度提交
                                          ≈ 每 5 分钟

另外还有:

神策 Sensors Data
↓
sawebjssdkpageleave
↓
页面停留 / 用户行为统计

属于第三套统计体系。

所以这个网页至少同时涉及:

华医业务系统
+
POLYV 视频系统
+
神策行为分析系统
+
华为云 WAF

一开始看 Network 很乱,就是因为实际上同时存在四层东西。


13. 这次最值得保留的逆向方法

这次最有价值的不是华医网本身,而是你实际上走完了一套很标准的黑盒 Web 排障流程:

现象
↓
提出多个假设
↓
Cookie / LocalStorage
↓
识别第三方组件
↓
Network 分类
↓
暂停 / 播放做对照实验
↓
区分媒体流量和业务流量
↓
找到核心业务接口
↓
比较成功 / 失败响应
↓
检查客户端凭据是否仍存在
↓
记录请求时间线
↓
发现“应该发生但没有发生”的事件
↓
定位浏览器生命周期
↓
修改单一变量
↓
长时间实验验证

尤其有三个思维点非常值得记住:

第一,关注“缺失的事件”。
真正破案的不是 401,而是:

本该每 5 分钟一次的 addC,为什么 70 分钟完全没出现?

这是比错误本身更强的信号。

第二,不要看到 heartbeat 就认为是保活。
这次有:

神策 5 秒 heartbeat
POLYV 播放统计
华医课程状态轮询
华医学习进度提交

都可以被口语叫作“心跳”,但含义完全不同。

第三,第三方资源正常不代表主业务正常。

视频还在播

只能说明:

POLYV 正常

不能证明:

华医登录正常
学习进度正常

这种“多个子系统独立运行”的问题在真实 Web 系统里非常常见。


这次其实已经不只是“找一个掉登录原因”了,更像是你第一次比较完整地做了一轮基于浏览器 DevTools 的黑盒系统分析:从 Cookie、LocalStorage、HTTP、第三方 SDK、业务接口一直追到浏览器生命周期,最后再用对照实验验证根因。

而这套思路以后无论调 FastAPI 前后端问题、浏览器插件、第三方 API,还是线上系统故障,都能直接复用。

继续完善这次逆向记录

  • 画出完整请求时序图
  • 整理成一页技术报告