pangu-user-platform/docs/05-模块技术方案/开放API-学生列表授权-技术设计方案.md

6.6 KiB
Raw Permalink Blame History

开放API-学生列表授权 技术设计方案

作者pangu
创建时间2026-02-04
评审状态:待评审


一、总体架构

1.1 请求链路

外部请求 GET /open/api/student/list?pageNum=1&pageSize=10
  → 不经过 Sa-Token 登录校验security.excludes 排除 /open/api/**
  → ApiAuthInterceptor 拦截 /open/api/**
      → 校验 X-App-Id、X-Timestamp、X-Sign
      → 查应用信息、校验接口授权
  → OpenApiStudentController.list() → IPgStudentService.selectPageList()
  → 返回 TableDataInfo<StudentVo>

1.2 模块与职责

模块/组件 职责
Security 配置 /open/api/** 加入排除路径,不校验登录
ApiAuthInterceptor 仅对 /open/api/** 生效:校验头、签名、接口授权
OpenApiStudentController 提供 GET /open/api/student/list,委托现有 StudentService
IPgApplicationService 新增 selectByAppCode、checkApiPermission新增/编辑时同步 pg_app_api
PgAppApiMapper 新增按 appCode 查授权 api_path 列表方法(联表 pg_api_dict
pg_api_dict 增加「学生列表」开放接口记录api_path=/open/api/student/list

二、安全与鉴权

2.1 请求头约定

请求头 必填 说明
X-App-Id 应用编码appCode
X-Timestamp 当前时间毫秒时间戳,防重放
X-Sign 签名,见 2.2

2.2 签名算法

  1. 将请求参数Query 与 Body仅一层 key-value按参数名 ASCII 升序排序。
  2. 拼接为:key1=value1&key2=value2&...&appSecret=应用密钥appSecret 为服务端存储的密钥)。
  3. 对上述字符串做 MD5,结果转为 大写,即 X-Sign。

示例GET无 body

  • 请求GET /open/api/student/list?pageNum=1&pageSize=10
  • 假设 appSecret=abc123
  • 参数字符串:pageNum=1&pageSize=10&appSecret=abc123
  • sign = MD5(参数字符串).toUpperCase()

2.3 时间戳防重放

  • 服务端收到 X-Timestamp 后,与当前服务器时间比较,若 |now - timestamp| > 5 分钟,返回 400「请求已过期」。

2.4 接口授权校验

  • 请求 URI 取 path/open/api/student/list),与当前应用在 pg_app_api + pg_api_dict 中关联的 api_path 集合比对;不在集合内则返回 403「无权访问该接口」。

三、接口设计

3.1 开放接口:学生列表

项目 说明
方法 GET
路径 /open/api/student/list
鉴权 应用签名 + 接口授权
参数 与现有 /business/student/list 一致studentName、studentNo、schoolId、schoolGradeId、schoolClassId、status 等;分页 pageNum、pageSize
响应 与现有一致:{ code, msg, rows, total }TableDataInfo

3.2 错误响应

HTTP 状态 场景 示例 msg
400 缺少认证参数 / 时间戳格式错误 / 请求已过期 缺少认证参数
401 应用不存在 / 应用已停用 / 签名验证失败 签名验证失败
403 未授权该接口 无权访问该接口

四、数据与缓存

4.1 表使用

  • pg_application:已有;需按 app_code 查询selectByAppCode
  • pg_api_dict:已有;需新增一条 api_path=/open/api/student/list 的记录,供授权勾选与校验。
  • pg_app_api:已有;新增/编辑应用时按 apiCodes 写入/更新;鉴权时按 app_id 联表 pg_api_dict 得到 api_path 列表。

4.2 授权生效

  • 方案 A每次请求查库或查缓存
  • 方案 B应用授权变更时更新缓存校验时先查缓存。
    本期可采用不缓存短 TTL如 5 分钟)缓存「应用授权 path 集合」,技术实现时在 Service 中按 appCode 查授权 path 列表即可;若后续性能有要求再加 Redis。

五、实现清单

5.1 配置

  • application.ymlsecurity.excludes 增加 /open/api/**

5.2 应用模块application

  • IPgApplicationService:新增 PgApplication selectByAppCode(String appCode)boolean checkApiPermission(String appCode, String apiPath)
  • PgApplicationServiceImpl实现上述方法insert/update 时增加对 apiCodes 的处理,同步 pg_app_api先删后插
  • PgApplication:增加 @TableField(exist = false) private List<String> apiCodes;,用于接收前端与返回详情。
  • PgAppApiMapper:新增方法,如 List<String> selectApiPathsByAppCode(String appCode)(联表 pg_api_dict 取 api_path
  • PgAppApiMapper.xml:对应 SQLa.app_id = app_id 且 a.api_id = d.api_idd.api_path

5.3 开放 API 模块(新建或放在 business 下)

  • OpenApiConfigWebMvcConfigurer注册 ApiAuthInterceptor仅 addPathPatterns("/open/api/**")。
  • ApiAuthInterceptorpreHandle 中取 X-App-Id、X-Timestamp、X-Sign校验时间戳 → selectByAppCode → 校验签名 → checkApiPermission(uri) → 通过则 return true。
  • OpenApiStudentControllerGET /open/api/student/list,参数与 PgStudentController.list 一致,委托 IPgStudentService.selectPageList。

5.4 接口字典数据

  • 在 pg_api_dict 中 INSERT 一条api_code=OPEN_STUDENT_LISTapi_name=学生列表api_path=/open/api/student/listapi_method=GETstatus=0。可使用 SQL 脚本或启动时初始化。

5.5 前端

  • 已有接口授权勾选apiCodes与 apiList 接口;只需保证后端保存/回显 apiCodes 与 pg_app_api 一致即可,无需改前端逻辑(若当前未回显 apiCodes需后端在查询应用详情时填充 apiCodes

六、调用测试说明

  • 提供「开放API-学生列表授权-调用测试说明.md」或同目录下测试脚本
    • 签名算法说明与示例(含 GET 示例)。
    • 示例:在管理端创建应用并勾选「学生列表」,记录 appCode、appSecret用 curl 或 Postman 调用 GET /open/api/student/list携带正确 X-App-Id、X-Timestamp、X-Sign。
    • 预期:授权应用返回 200 与列表数据;未授权或错误签名返回 401/403。

七、评审要点

  1. 开放路径仅限 /open/api/**,与现有 /business、/h5 隔离,且不校验用户登录。
  2. 签名算法与请求头约定是否满足安全与对接方实现成本平衡。
  3. 学生列表参数与响应与现有一致,避免业务逻辑重复。
  4. 应用管理侧保存/回显接口授权与 pg_app_api、pg_api_dict 数据一致性。
  5. 后续扩展其他开放接口时,仅需在 pg_api_dict 加记录、新增对应 OpenApiXxxController 即可,鉴权与拦截器复用。