思考与分析
公开教程 · 无需登录

思考与分析

课程原型的对外路径带有 /week03 前缀,浏览器中实际请求为 /week03/api/...;Go 进程内的路由仍为 /api/...。记录时以开发者工具中观察到的实际请求为准。口令不得写入任何公开材料。

1登录请求的参数、响应与完整 HTTP 报文

分析思路

在开发者工具中定位登录对应的 HTTP 请求,按报文结构逐项记录,不要仅保留页面截图:

  • 请求行:方法为 POST,请求目标为本站登录路径。
  • 请求头:Content-Type 为 JSON。
  • 请求体:包含用户名与口令字段。书面材料中须对口令脱敏。
  • 状态码。
  • 响应头:认证成功时包含 Set-Cookie。记录名称、HttpOnly、SameSite、Path。
  • 响应体:认证成功时返回用户标识、用户名、角色与所属班级。不得包含口令明文或口令哈希。

再分别使用错误口令与不存在的用户名各请求一次,比较状态码与响应体是否一致。账号不存在、口令错误与触发限流,对外响应应保持同构,真实原因仅写入服务端日志。数据库不可用属于依赖故障,虽可与认证失败区分,但仍不得据此判断账号是否存在。

请求 请求行:POST 登录路径 请求头:Content-Type: application/json 请求体:username、password 书面材料中须对口令脱敏 响应(认证成功) 状态码:200 Set-Cookie:名称、HttpOnly、SameSite、Path 响应体:id、username、role、class 不含口令明文,也不含口令哈希 账号不存在、口令错误、触发限流:对外返回同一状态码与同一响应体
按请求行、请求头、请求体、状态码、响应头、响应体六个部分记录。三种认证失败原因对外保持同构。

2认证成功之后,服务端如何标识用户身份

分析思路

观察认证成功之后的下一个受保护请求,例如材料列表。该请求的请求体中不应再出现用户名与口令;身份凭据由浏览器依据 Cookie 作用域自动附带。

在 Set-Cookie 中核对三项:Cookie 名称、是否设置 HttpOnly、Path 的作用范围。Cookie 值为随机会话标识,其中不含角色,也不含班级。数据库中保存的是该标识的摘要值,而非标识原文。

后续请求由认证中间件依据该标识查找会话,再从 users 表读取角色与 class_id。请在请求头、查询参数或 JSON 正文中自行加入 role 或 class_id,观察列表与上传结果是否改变——结果不应改变。GET /api/me 仅决定前端是否渲染上传入口,这属于可发现性(discoverability),并非授权;授权在服务端的角色校验中完成。

POST 登录 校验口令哈希 Set-Cookie 仅含随机会话标识 后续请求 自动附带 Cookie 查 sessions 再读 users role、class_id 请求中自行提交的 role、class_id 不参与判定 GET /api/me 仅影响前端入口渲染;授权发生在服务端角色校验
会话标识存放于 Cookie,角色与班级存放于用户表。客户端自行声明的身份不会覆盖服务端读取到的值。

3文件上传的处理流程,以及 .txt、.md 之外是否允许

分析思路

先以学生身份提交上传,确认请求在哪一层被拒绝,以及磁盘目录与 materials 表是否出现新记录。学生请求应在角色授权环节被拒绝,此时文件尚未写入。

再以教师身份分别提交 .txt、.md,以及一种白名单之外的扩展名(或仅将文件名后缀改为 .pdf)。服务端按扩展名白名单校验,本课仅接受 .txt 与 .md。校验失败的请求应返回错误响应,且不产生文件,也不产生孤立的材料记录。

校验通过时,材料记录与知识库正文应在同一事务中提交。写入的 class_id 取自会话,而非表单字段。存储文件名由服务端生成,不使用客户端提供的原始文件名作为路径。

学生上传 角色校验拒绝,文件尚未写入 教师,白名单外扩展名 扩展名校验失败,无文件、无记录 教师,.txt / .md 服务端生成存储名,class_id 取自会话 材料行与知识库正文在同一事务中提交
白名单之外的扩展名在落盘之前即被拒绝。成功路径上,文件与两张表要么一并留存,要么均不留存。

4文件下载是否需要授权,以及实现位于何处

前端是否渲染下载按钮仅影响可发现性,不能作为授权机制是否存在的验收标准。

分析思路

在网络面板中记录一次下载请求的目标地址。其路径应为 /api/materials/{id}/file,而非上传目录的静态 URL。随后比较三次请求的状态码,以及响应体是否包含文件内容:

  • 未认证,或已删除 Cookie 后重放该请求。
  • 已认证,且材料属于当前会话对应的班级。
  • 沿用该会话,将路径中的材料标识替换为其他班级的材料标识。

未认证的请求应被认证中间件拒绝。仅当班级一致时才读取文件。跨班级访问与资源不存在对外均返回同一 404,避免以状态码泄露资源是否存在;差异仅记入服务端日志。上传目录仅挂载到 api 容器,Nginx 不将其配置为静态资源目录。

未认证,或已删除 Cookie 认证中间件拒绝,不读取文件 已认证,且属于本班 GET /api/materials/{id}/file 返回文件 跨班级,或记录不存在 对外均返回同一 404,真实原因仅记日志
下载须经受保护接口。上传目录并非静态资源,仅掌握存储路径也无法绕过班级归属校验。

5后端 API 是否对客户端暴露,以及如何直接调用

应将「是否暴露」拆分为两个独立问题,不宜以一个是或否来回答:

  • api 容器的端口是否映射到宿主机,从而使客户端可绕过反向代理直连 Go 进程?
  • 浏览器能否调用业务接口?若可以,其请求的主机与路径分别是什么?

分析思路

查看 Compose 的端口发布配置。本课仅 web 向浏览器发布端口,api 与数据库仅存在于容器网络内部。再在开发者工具中查看登录或材料列表请求的主机与路径:应为同源的 /api(课程站点上带 /week03 前缀),而非 Go 进程的监听端口。

Nginx 将以 /api 开头的请求反向代理至 Go,自身不执行认证。因此客户端调用的是本站接口,而非 api 容器的直连地址。直接调用时,应向本站 /api/... 发送 HTTP 请求;材料列表等受保护资源还须携带认证成功后的 Cookie。GET /health 不经过认证,仅用于存活探测,其响应中不包含材料数据。

浏览器 唯一入口 web · Nginx 发布端口 api · Go 不映射宿主机端口 /api 由 Nginx 反向代理 MySQL 不映射宿主机端口 直接调用即请求本站 /api;受保护资源须携带 Cookie;/health 仅用于存活探测
接口对客户端可达,是因为反向代理将 /api 转发至 Go;api 进程本身不向宿主机发布端口。