From cb40524b8e23ac133a39f4a40449526c1f104f99 Mon Sep 17 00:00:00 2001 From: chenxing Date: Wed, 21 Oct 2026 08:52:42 +0800 Subject: [PATCH] =?UTF-8?q?feat(oapi):=20user/modules=20=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E5=8F=AF=E9=80=89=20appCode=EF=BC=8C=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E6=9F=A5=E8=AF=A2=E6=8C=87=E5=AE=9A=E5=BA=94=E7=94=A8=E7=9A=84?= =?UTF-8?q?=E6=A8=A1=E5=9D=97=E6=A0=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 传 appCode => 按 application_info.app_code 解析目标应用(供托管型应用代渲染导航) - 未传 => 保持原行为,按验签 appKey 解析调用方应用 - 应用解析失败一律返回空列表(安全优先,禁止意外泄露其他应用模块) - 参数用 appCode 而非整数 appId:跨环境稳定,与 form-center 的 /app/{appCode}/ 同键 --- .../controller/oapi/UserController.java | 38 ++++++++++++++----- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/yunhai/userbase/controller/oapi/UserController.java b/src/main/java/com/yunhai/userbase/controller/oapi/UserController.java index fe7c534..7cb6226 100644 --- a/src/main/java/com/yunhai/userbase/controller/oapi/UserController.java +++ b/src/main/java/com/yunhai/userbase/controller/oapi/UserController.java @@ -10,6 +10,7 @@ import com.yunhai.platform.utils.model.BoaUser; import com.yunhai.userbase.common.annotation.OapiLog; import com.yunhai.userbase.common.validation.ValidationGroups; import com.yunhai.userbase.config.Config; +import com.yunhai.userbase.entity.ApplicationInfo; import com.yunhai.userbase.model.sysuser.BatchChangeStatusRequest; import com.yunhai.userbase.model.sysuser.SysUserChangeStatusRequest; import com.yunhai.userbase.model.sysuser.SysUserContactRequest; @@ -91,30 +92,49 @@ public class UserController { } /** - * 查询用户在调用方应用下、指定学校(上下文)里的已授权模块列表。
+ * 查询用户在指定应用下、指定学校(上下文)里的已授权模块列表。
*
- * 与管理端「权限一览」返回全应用聚合不同,本方法严格按调用方应用维度过滤:
- * ① 通过 {@link AppAuthFilter#APP_AUTH_ATTR} 拿到验签时的 appKey(RequireAppSignFilter - * 已保证非空);
- * ② 查 application_info 映射到具体 appId;
+ * 与管理端「权限一览」返回全应用聚合不同,本方法严格按单一应用维度过滤。应用来源两态:
+ * ① 传了可选参数 appCode → 按 application_info.app_code 解析目标应用(供托管型应用 + * 如 form-center 代快应用渲染导航时查询目标应用的模块清单;查不到返回空列表并 warn, + * 口径同现有兜底);
+ * ② 未传 appCode → 现行为不变:通过 {@link AppAuthFilter#APP_AUTH_ATTR} 拿到验签时的 + * appKey(RequireAppSignFilter 已保证非空),查 application_info 映射到具体 appId;
* ③ 调 Service 专用方法:所有 SQL/集合构建均限定在该 appId 范围内(数据库层面过滤)。
*
- * 边界:调用方 appKey 查不到对应应用 → 直接返回空列表(安全优先,禁止意外返回其他应用的模块)。
+ * 边界:应用解析失败(appCode/appKey 查不到对应应用)→ 直接返回空列表(安全优先,禁止意外 + * 返回其他应用的模块)。
+ * 记账:本参数暴露面=模块名/路径/图标级低敏数据;如需收紧可后续限定仅 BASE 应用调用 + * (暂放开,表单中心建设方案 §6.2 已记)。参数用 appCode(字符串)而非整数 appKey: + * 跨环境稳定,与 form-center 分发路由 /app/{appCode}/ 同键。 * - * @param request 用于取 AppAuthFilter 设置的 appKey + * @param request 用于取 AppAuthFilter 设置的 appKey(未传 appCode 时使用) * @param userId 用户ID(必填) * @param schId 学校ID/组ID(可选;用于限定「学校上下文」:非空时仅取该学校子树内的真实归属组参与判定, * 为空时取用户全部真实归属组,与权限一览一致) + * @param appCode 目标应用技术标识(可选;传=查该应用的模块树,不传=按签名调用方解析) * @return 该应用下用户已授权模块(扁平化;无权限时返回空列表) */ @GetMapping("modules") public List modules(HttpServletRequest request, @RequestParam("userId") Integer userId, - @RequestParam(value = "schId", required = false) Integer schId) { + @RequestParam(value = "schId", required = false) Integer schId, + @RequestParam(value = "appCode", required = false) String appCode) { if (userId == null) { return Collections.emptyList(); } - Integer appId = resolveCallerAppId(request); + Integer appId; + if (appCode != null && !appCode.trim().isEmpty()) { + ApplicationInfo app = applicationInfoService.getByAppCode(appCode.trim()); + if (app == null) { + log.warn("[oapi:user:modules] appCode={} not found in application_info, uri={}", + appCode, request.getRequestURI()); + return Collections.emptyList(); + } + appId = app.getId(); + } else { + appId = resolveCallerAppId(request); + } if (appId == null) { return Collections.emptyList(); }