feat(system): 按 OAuth2 客户端 platform 过滤菜单,支持业务/物联双前端

问题:业务平台(biz)和物联运维平台(iot)共用一套用户体系和菜单表,但每个前端
只该看到自己域的菜单。原来没有按客户端过滤的机制。

方案:在 OAuth2 客户端维度打 platform 标签(biz/iot/NULL),菜单也打同样标签,
登录时下发菜单按二者匹配过滤。

链路:
- OAuth2AccessTokenCheckRespDTO / LoginUser(framework + gateway)新增 clientId 字段
- TokenAuthenticationFilter(framework + gateway)把 accessToken.clientId 带进 LoginUser
- WebFrameworkUtils.HEADER_CLIENT_ID="X-Client-Id":登录/refresh 等"无 token 入口"
  允许前端声明 client,避免硬编码 default
- AdminAuthServiceImpl.resolveClientId:未传 Header 时回退 OAuth2ClientConstants.CLIENT_ID_DEFAULT
- MenuDO / OAuth2ClientDO 各加 platform 列
- MenuService.filterMenusByPlatform:platform 为空(全平台共用)或匹配即保留

SQL 迁移按字母序编号:
- _01_oauth2_client_platform.sql:加列 + 给 default/iot-client 客户端打标 + 递归标
  IoT 菜单子树(root id=4000)为 iot
- _02_bulk_mark_biz_menus.sql:其余 platform=NULL 的菜单兜底标 biz
- 顺序依赖:_01 标完 iot 后 _02 才动剩余 NULL,避免 _02 把 IoT 菜单错标 biz

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
lzh
2026-04-24 13:32:26 +08:00
parent 4564eec893
commit cbbb048a4d
15 changed files with 729 additions and 559 deletions

View File

@@ -43,6 +43,14 @@ public class LoginUser {
* 授权范围
*/
private List<String> scopes;
/**
* OAuth2 客户端编号
*
* 用于区分用户从哪个前端登录进来,做按客户端的菜单/权限过滤。客户端 → platform 映射:
* - {@code default}(业务平台) → platform=biz
* - {@code iot-client}(物联运维平台) → platform=iot
*/
private String clientId;
/**
* 过期时间
*/

View File

@@ -97,7 +97,8 @@ public class TokenAuthenticationFilter extends OncePerRequestFilter {
return new LoginUser().setId(accessToken.getUserId()).setUserType(accessToken.getUserType())
.setInfo(accessToken.getUserInfo()) // 额外的用户信息
.setTenantId(accessToken.getTenantId()).setScopes(accessToken.getScopes())
.setExpiresTime(accessToken.getExpiresTime());
.setExpiresTime(accessToken.getExpiresTime())
.setClientId(accessToken.getClientId()); // OAuth2 客户端编号,用于按前端来源过滤菜单
} catch (ServiceException serviceException) {
// 校验 Token 不通过时,考虑到一些接口是无需登录的,所以直接返回 null 即可
return null;