Files
shz-admin/docs/pc-jobseeker-api-audit.md
lapuda b4b17f2ada
Some checks failed
Node CI / build (14.x, macOS-latest) (push) Has been cancelled
Node CI / build (14.x, ubuntu-latest) (push) Has been cancelled
Node CI / build (14.x, windows-latest) (push) Has been cancelled
Node CI / build (16.x, macOS-latest) (push) Has been cancelled
Node CI / build (16.x, ubuntu-latest) (push) Has been cancelled
Node CI / build (16.x, windows-latest) (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
coverage CI / build (push) Has been cancelled
Node pnpm CI / build (16.x, macOS-latest) (push) Has been cancelled
Node pnpm CI / build (16.x, ubuntu-latest) (push) Has been cancelled
Node pnpm CI / build (16.x, windows-latest) (push) Has been cancelled
feat: 添加小程序二维码标题和体验版提示信息
2026-07-23 17:23:13 +08:00

21 KiB
Raw Blame History

PC 求职者端接口调用深度审查

审查日期2026-07-23
审查对象:shz-admin 中由 PC 求职者路由、其共享头部组件及求职者专用弹窗实际引用的接口调用。
本文是前端静态调用清单,并用测试环境匿名首页的实际网络记录交叉验证;不代表后端已对每个接口的权限、字段和错误码逐一验收。

1. 结论摘要

项目 结果
求职者路由数 21 条(首页、职位、招聘会、简历、个人中心、政策、直播等)
已识别的不同“方法 + 路径”调用 60 条
/cms/ 类调用 33 条
/cms/ 的 App / SSO / 会话调用 27 条
直接 fetch / axios 调用 未发现;页面统一通过 Umi request 发起
风险结论 求职者端大量直接调用 /cms/ 路径其中包含个人资料、收藏、投递、投诉、消息、屏蔽企业、字典、行业及政策导出。路径命名与“CMS 是后台接口”的边界不一致,需要后端按求职者身份做强制鉴权和资源归属校验。

最重要的审查发现:/cms/ 不能仅凭前端路径前缀被视为“后台专用且不会被求职者调用”。本项目的 PC 求职者代码已实际引用 33 个此类端点。若后端仅凭 URI 前缀而不是用户角色、数据归属和操作权限做控制,会形成越权风险。

2. 审查范围与方法

覆盖范围

  1. 路由定义:config/routes.ts/job-portal/** 分支。
  2. 页面代码:src/pages/JobPortal/**
  3. 求职者页面必经的共享组件:
  4. 由上述页面/组件导入的 serviceservices/jobportalservices/common/jobTitleservices/cms/policyInfoservices/cms/jobComplaintservices/classify/industryservices/system/dictservices/Management/listservices/session 与 SSO 辅助模块。

识别规则

  • request(...)、下载请求和页面内直接 request(...) 为接口调用源。
  • 仅列出从求职者路由实际可达的调用;同一 URI 的不同 HTTP 方法视为不同接口。
  • CMS 类接口:请求字符串中包含 /cms/。本分类基于调用路径,不推断后端最终的角色授权。
  • 纯管理端 service 中未被求职者页面导入的方法不纳入清单。

生产路径换算

src/app.tsx 为生产请求配置了当前域名下的 /api/shihezi/ 基址,并会将源码请求参数开头的 /api 去掉。因此表中的“源码 request 路径”会在测试环境实际成为:

源码:/api/cms/job/recommend
实际https://test.xjshzly.longbiosphere.com/api/shihezi/cms/job/recommend

源码:/app/job/123
实际https://test.xjshzly.longbiosphere.com/api/shihezi/app/job/123

文中使用源码路径,方便与前端代码逐字对照;部署时请统一在此前面补上 /api/shihezi

3. 测试环境实测证据

入口

  • 用户提供的入口:https://test.xjshzly.longbiosphere.com/shihezi/
  • 恢复后最终路由:/shihezi/job-portal
  • 身份:未登录的 PC 求职者;没有读取浏览器 Cookie、Storage 或 Token也没有执行写操作。

首屏真实请求

源码请求路径 实际状态 关联代码 说明
GET /api/app/common/jobTitle/treeselect 502 首页、职位列表、简历、职位选择组件 职位树加载失败。
GET /api/cms/job/recommend?order=2 502 首页、全局头部、职位列表 热门职位加载失败。

页面在请求失败时仍呈现示例职位卡片,属于前端兜底展示,不能视为接口成功返回真实业务数据。该 502 是审查时刻的测试环境可用性快照,不影响下文静态调用覆盖结论。

4. 求职者路由与功能边界

路由 页面 核心功能 调用类别
/job-portal 首页 职位分类、热门推荐 App + CMS
/job-portal/list 职位列表 搜索、职位详情预览、收藏/投递/投诉/屏蔽 App + CMS
/job-portal/detail 职位详情 详情、竞争力、收藏/投递/投诉/屏蔽 App + CMS
/job-portal/job-fair 招聘会 线上与线下招聘会列表、日历与摘要 App
/job-portal/job-fair/detail 招聘会详情 招聘会详情、线上关联岗位 App
/job-portal/resume 简历 基础资料、工作经历、技能维护 App + CMS
/job-portal/personal-center 个人中心 个人统计与资料展示 CMS
/job-portal/personal-center/applications 已投递 已投岗位 CMS
/job-portal/personal-center/favorites 收藏 收藏列表、取消收藏 CMS
/job-portal/personal-center/footprints 足迹 浏览足迹 CMS
/job-portal/personal-center/showcase 我的风采 文件上传、查询、删除 App
/job-portal/personal-center/interviews 面试邀约 查询、接受/拒绝 App
/job-portal/personal-center/complaints 我的投诉 查询、撤销投诉 CMS
/job-portal/personal-center/blocked-companies 屏蔽企业 查询、解除屏蔽 CMS
/job-portal/message 消息 查询、已读、删除、全部已读 CMS
/job-portal/career-recommendation 职业推荐 读取当前求职者资料 CMS
/job-portal/policy 政策列表 门户政策、按用户类型筛选、下载 App + CMS
/job-portal/policy/detail 政策详情 门户政策详情 App + CMS
/job-portal/live-recruitment 直播带岗 直播列表 App
/job-portal/profile 我的 读取客户端缓存展示,未发现新增远程请求 无新增

JobPortalHeader 会被绝大多数页面复用,因而其中的职位联想、热门职位和登录后消息未读数请求是跨路由调用

5. CMS 类接口清单33 条)

这是本次审查的重点。所有条目均由求职者端页面或其共用组件导入并调用;“需登录”表示前端存在登录/用户 ID 前置校验或该功能显然依赖个人数据,最终权限仍必须由后端校验。

ID 方法与源码 request 路径 用途与主要参数/请求体 调用页面/组件 登录/触发条件
C01 GET /api/cms/job/recommend 职位推荐;order=2 为热门,order=0,isPublish=1,jobCategory 为分类列表;可带 jobTitle 首页、职位列表、JobPortalHeader 公共浏览
C02 POST /api/cms/userworkexperiences 新增工作经历:companyName,position,startDate,endDate,description 简历 需登录
C03 GET /api/cms/appUser/getUserInfo 当前求职者资料 职业推荐、简历、登录身份预取 需登录
C04 GET /api/cms/appUser/getMyTj 我的投递/收藏等统计 个人中心 需登录
C05 GET /api/cms/job/getAppUserYhsq 已投递岗位;userId 已投递 需登录
C06 GET /api/cms/job/getAppUserYhsc 收藏岗位;userId 收藏 需登录
C07 GET /api/cms/job/getAppUserYhfwzj 浏览足迹;userId 足迹 需登录
C08 POST /api/cms/job/collection 收藏岗位:jobId,userId 职位列表、职位详情 需登录
C09 POST /api/cms/job/collectionCancel 取消收藏:jobId,userId 职位列表、职位详情、收藏 需登录
C10 PUT /api/cms/jobApply 投递岗位:jobId,userId 职位列表、职位详情 需登录
C11 POST /api/cms/job/browse 记录职位浏览:jobId 职位列表、职位详情 需登录后触发
C12 GET /api/cms/notice/appNoticList 消息列表;pageNum,pageSize,noticeType,isRead 消息 需登录
C13 GET /api/cms/notice/appNoticReadList 未读消息数量/列表 消息 需登录
C14 GET /api/cms/notice/noticTotal 消息总数与未读数 消息、全局头部 需登录
C15 GET /api/cms/notice/appNoticYdList 已读消息列表;pageNum,pageSize 消息 需登录
C16 POST /api/cms/notice/read/sysNotice?id={id} 标记单条消息已读 消息 需登录
C17 POST /api/cms/appUser/markAllMessagesRead 标记全部消息已读 消息 需登录
C18 DELETE /api/cms/notice/deleteNotice/{id} 删除消息 消息 需登录
C19 POST /api/cms/jobComplaint 提交岗位投诉:userId,jobId,complaintType,complaintContent,contactPhone 职位投诉弹窗 需登录
C20 POST /api/cms/blockCompany 屏蔽企业:userId,companyId 职位列表、职位详情 需登录
C21 GET /api/cms/blockCompany/list 屏蔽企业列表;pageNum,pageSize 屏蔽企业 需登录
C22 DELETE /api/cms/blockCompany/{id} 解除指定屏蔽记录 屏蔽企业 需登录
C23 GET /api/cms/dict/data/type/{dictType} CMS 字典值;详见下一节的字典类型 首页、职位、简历、个人中心、投诉、政策 公共展示/依页面而定
C24 GET /api/cms/dict/jobCategory 简历技能分类联想;name 简历 需登录
C25 GET /api/cms/industry/treeselect 行业树 职位列表、职位详情 公共浏览
C26 PUT /api/cms/appUser 更新简历基础信息/求职意向 简历 需登录
C27 DELETE /api/cms/userworkexperiences/{id} 删除工作经历 简历 需登录
C28 PUT /api/cms/userworkexperiences 编辑工作经历;含 id 与经历字段 简历 需登录
C29 PUT /api/cms/appskill/edit 修改技能:id,userId,name,levels 简历 需登录
C30 POST /api/cms/appskill/add 新增技能:name,levels 简历 需登录
C31 GET /api/cms/jobComplaint/listSelf 当前用户投诉列表;currentPage,pageSize,complaintType,complaintStatus 我的投诉 需登录
C32 DELETE /api/cms/jobComplaint/{id} 撤销投诉 我的投诉 需登录
C33 GET /api/cms/policyInfo/exportUtil?id={id} 下载政策文件,响应为 Blob 政策列表 公共入口可见,后端应决定下载权限

C23求职者端实际请求的 CMS 字典类型

getDictValueEnum(type, isDigital, true) 会固定落到 C23。已从调用点枚举到

字典类型 使用位置
company_nature 首页、职位列表、职位详情
education 职位列表、简历、个人中心
scale 职位列表、职位详情
sex 简历、个人中心
area 简历、个人中心
age 简历、个人中心
complaint_type 投诉弹窗、我的投诉
user_type 政策列表、政策详情
policy_category 政策列表

实现依据:getDictValueEnum行业树 service求职者 user service

6. 非 CMS 接口清单27 条)

ID 方法与源码 request 路径 用途与主要参数/请求体 调用页面/组件 登录/触发条件
P01 GET /api/app/common/jobTitle/treeselect 职位/职位分类树 首页、职位列表、简历、职位选择组件 公共浏览
P02 GET /api/app/job/suggest 搜索联想:keyword,limit JobPortalHeader 输入关键词时
P03 GET /app/job/{jobId} 职位详情,注释说明含 isApply,isCollection 职位列表、职位详情 公共详情;状态字段依登录态
P04 GET /app/job/competitiveness/{jobId} 职位竞争力、匹配分、排行、雷达数据 职位列表、职位详情 需登录,前端未登录时不请求
P05 POST /app/file/upload?bussinessid={userId} 上传“我的风采”文件,multipart/form-data 我的风采 需登录、用户选择文件
P06 GET /app/file/list 查询“我的风采”文件;bussinessid 我的风采 需登录
P07 DELETE /app/file/{id} 删除“我的风采”文件 我的风采 需登录
P08 DELETE /api/app/appskill/{id} 删除技能 简历 需登录
P09 GET /api/app/interview/list 我的面试邀约;userId 面试邀约 需登录
P10 PUT /api/app/interview/status/{id} 接受/拒绝邀约;请求体 {status} 面试邀约 需登录
P11 GET /api/app/policyInfo/portalList 门户政策列表 政策列表 公共浏览
P12 GET /api/app/policyInfo/detail/{id} 门户政策详情 政策详情 公共浏览
P13 GET /api/app/live/list 直播带岗列表;可带 title,companyName 直播带岗 公共浏览
P14 GET /app/jobfair/public/jobfair/page 线上招聘会分页;pageNum,pageSize,jobFairTitle,zphjbsj 招聘会 公共浏览
P15 GET /app/outdoor-fair/page 线下招聘会分页;参数同 P14 招聘会 公共浏览
P16 GET /app/jobfair/public/jobfair/dates 线上招聘会日期集合 招聘会 公共浏览
P17 GET /app/outdoor-fair/dates 线下招聘会日期集合 招聘会 公共浏览
P18 GET /app/jobfair/public/jobfair/currentMonth 当月线上招聘会摘要 招聘会 公共浏览
P19 GET /app/outdoor-fair/currentMonth 当月线下招聘会摘要 招聘会 公共浏览
P20 GET /app/jobfair/public/jobfair/currentQuarter 本季度线上招聘会摘要 招聘会 公共浏览
P21 GET /app/outdoor-fair/currentQuarter 本季度线下招聘会摘要 招聘会 公共浏览
P22 GET /app/jobfair/public/jobfair/detail 线上招聘会详情;jobFairId 招聘会详情 公共浏览
P23 GET /app/outdoor-fair/{jobFairId} 线下招聘会详情 招聘会详情 公共浏览
P24 GET /app/jobfair/public/jobfair/enterprises-with-jobs-by-job-fair-id 线上招聘会企业及关联岗位;jobFairId 招聘会详情 仅线上招聘会
P25 POST /api/sso/pcms/code/login 第三方 code 换取会话;请求体 {code},不附现有 Token 根路径/login-tow 的 SSO 过渡 URL 含 code
P26 POST /api/sso/pc/code/login 第三方 token 换取会话;请求体 {code: token},不附现有 Token 根路径/login-tow 的 SSO 过渡 URL 含 token
P27 GET /api/getInfo 当前会话用户资料;首页在“有 Token 但缓存未就绪”时补拉 首页、身份预取 有有效会话

实现依据:职位通用 service招聘会 service竞争力 service政策门户 serviceSSO service

7. 调用链和登录边界

浏览器 /shihezi/
  └─ 前端路由 /job-portal
      ├─ 全局头部P02、C01登录后还会调用 C14
      ├─ 首页P01、C01、C23有会话且缓存缺失时 P27
      ├─ 职位列表/详情P01、P03、P04、C01、C08~C11、C19、C20、C23、C25
      ├─ 简历P01、P08、C02、C03、C23、C24、C26~C30
      ├─ 个人中心C03~C07、C21~C23、C31~C32、P05~P07、P09~P10
      ├─ 消息C12~C18
      ├─ 政策P11~P12、C23、C33
      ├─ 招聘会P14~P24
      ├─ 直播带岗P13
      └─ 第三方进入P25 / P26

前端会在收藏、投递、投诉、屏蔽、简历和个人中心入口执行登录/用户 ID 前置校验;实现见 jobPortalAuth.ts。这是用户体验保护,不是后端授权边界。特别是请求体中显式携带 userId 的 C05~C10、C19、C20 以及 P05/P06/P09后端必须从认证主体推导用户 ID或至少校验参数与认证主体一致。

8. 代码审查发现与建议

高优先级CMS 路径在求职者端广泛使用

  • 证据C01~C33 全部存在于求职者可达调用链,其中写操作至少包括收藏、取消收藏、投递、浏览记录、消息已读/删除、投诉、屏蔽、简历编辑、工作经历、技能维护及政策下载。
  • 风险:若网关/后端将 /cms/** 视为后台路由,可能导致求职端功能被错误拒绝;反过来,若为兼容而放宽 /cms/**,则容易放大管理后台能力暴露面。
  • 建议
    1. 优先为求职者能力建立 /app/**/portal/** 的专用 DTO、Controller 与权限策略;不要继续把“后台资源的 URI”作为前端契约。
    2. 迁移前,后端应按“求职者角色 + 资源归属”对全部 C05C10、C19C22、C26~C32 做强制校验。
    3. 迁移时保留短期兼容层,记录 /cms/** 的求职者访问量,确认无调用后再下线旧路由。

高优先级:userId 由客户端传入的水平越权面

涉及 C05~C10、C19、C20、P05、P06、P09。前端虽然通常从当前用户缓存读取 ID但浏览器请求可被篡改。

  • 后端不应信任请求中的 userId 来决定数据所有者。
  • 对“查看自己的投递/收藏/足迹/面试/文件”“增删自己的简历、投诉、屏蔽”等操作,应以认证 Token 的 subject 为准。
  • 若保留 userId 兼容字段,至少断言它等于当前登录求职者;不匹配时返回 403 并记录审计日志。

中优先级:方法与注释/命名不一致,容易误审权限

源码中存在多处注释与真实 HTTP 合约不一致:

  • favoriteJob 注释写为 /api/job-portal/list,真实为 POST /api/cms/job/collection
  • unfavoriteJob 注释写为 DELETE /app/company/card/collection,真实为 POST /api/cms/job/collectionCancel
  • applyJob 注释写 POST /api/cms/jobApply/apply,真实为 PUT /api/cms/jobApply
  • 多个消息函数的注释写 appUser/*,真实路径为 notice/*

建议以 OpenAPI/接口类型为单一事实来源,自动生成 service至少在服务函数名、JSDoc、HTTP 方法与实际 path 四者之间增加契约测试。

中优先级:匿名首页存在接口 502且 UI 回退会掩盖问题

审查时 P01 与 C01 均返回 502但首页仍展示静态示例岗位。建议

  • 生产环境不要把演示招聘数据作为接口失败的静默回退,改为明确的“加载失败/重试”状态。
  • 为 P01 和 C01 增加测试环境健康检查、网关 upstream 监控与告警。
  • UI 事件中记录去敏后的请求失败码和功能上下文,便于区分“暂无数据”与“网关故障”。

中优先级:政策下载混入 CMS 资源边界

政策列表/详情使用 P11/P12 门户接口,但下载使用 C33 的 /cms/policyInfo/exportUtil。建议提供对应的门户下载端点,并明确是否允许匿名下载、是否需要鉴权和审计。

低优先级:响应类型与权限语义需收敛

  • getJobDetail、文件接口等返回类型过于宽泛(如 API.Result 或未显式类型),会削弱调用端对字段与错误码的约束。
  • 建议将列表、详情、写操作、下载和分页响应定义为独立类型;对 401、403、404、409 和 5xx 在 UI 层做一致处理。

9. 后端复核清单

在后端/网关侧逐项核查以下内容,才能把本前端审查升级为完整安全验收:

  1. 对 C01~C33 所有实际路由,导出 Controller 映射、允许角色、认证注解和数据范围规则。
  2. 对所有含 userId 的 C05~C10、C19、C20、P05、P06、P09验证越权请求会得到 403而不会读取或写入他人数据。
  3. 对 C02、C26~C32 的写操作,确认审计字段(创建人、更新人、时间、操作来源)由服务端写入,不接受客户端伪造。
  4. 对 C33、P05~P07 的文件接口验证文件归属、下载授权、MIME/大小限制、路径穿越防护与病毒扫描策略。
  5. 对 P25/P26 的 SSO 换票,确认 code/token 一次性、时效、重放防护、来源校验和 Token 不出现在日志中。
  6. 修复/定位测试环境 P01、C01 的 502 后,再分别用匿名求职者和测试登录求职者做只读接口冒烟测试。

10. 可复现的审查命令

以下命令只读取代码,可用于后续补充或复核:

# 列出求职者路由
rg -n "job-portal" config/routes.ts

# 找出求职者页面直接发起的 request
rg -n "request\\(" src/pages/JobPortal src/components/JobPortalHeader \
  src/components/JobComplaintModal src/components/JobTitleSelector.tsx

# 找出涉及 CMS 的 service 调用
rg -n "['\\\"]/api/cms/" src/pages/JobPortal src/services/jobportal \
  src/services/common src/services/cms src/services/classify src/services/system src/services/Management

11. 未纳入范围

  • 纯管理端、企业端、系统工具和小程序的接口。
  • 后端 Controller、数据库、网关权限的逐条实现验证。
  • 已被 service 定义但没有被求职者可达页面调用的管理端接口。
  • 本次没有使用测试账号执行任何写接口;表中所有写操作均来自静态调用链审查。