# feat(user): SMS 登录限定制师 + active role 自动切 CUSTOMIZER > **仓库**: HL (后端 hl-user-service) > **关联 PR/Issue**: PR #2920, Closes #2918 > **日期**: 2026-05-22 > **影响范围**: 管理端 `POST /admin/auth/sms/login` SMS 验证码登录入口 > **接收方**: mmg (前端) > **前端**: **登录页前端无 API 改动**, 但 SMS 登录成功后 `data.role` 一定是 CUSTOMIZER (即使该 admin 还有其他角色) --- ## 🚨 关键变化 SMS 验证码登录入口 (`POST /admin/auth/sms/login`) **现在只允许有 `CUSTOMIZER` 角色的 admin 登录**, 且登录成功后强制以**定制师视角**颁发 token (即使 admin 还有 ADMIN/OPERATOR 等其他角色)。 业务原因: SMS 登录场景**仅定制师小程序分享**需要 (#2907), 其他角色不应该用 SMS 绕过 2FA 登入后台。 --- ## 一、API 影响 ### `POST /admin/auth/sms/login` 行为变化 | 场景 | 之前 | **现在** | |---|---|---| | 仅 CUSTOMIZER 角色 admin 用对码登录 | 颁发 token, `role` = `admin.currentRoleId` 对应的 roleKey | 颁发 token, **`role` 强制 = `CUSTOMIZER`** | | 多角色 admin (如 CUSTOMIZER+ADMIN) 用对码登录 | 颁发 token, `role` = `admin.currentRoleId` 对应的 roleKey | 颁发 token, **`role` 强制 = `CUSTOMIZER`** (无视 currentRoleId) | | **无 CUSTOMIZER 角色 admin** 用对码登录 | 颁发 token, `role` = 当前 active role | **返 200102 INVALID `验证码错误,剩余 N 次尝试`** + fail+1 (**防枚举**) | | mobile 不绑 admin 用对码登录 | 200102 INVALID (现有防枚举路径) | 200102 INVALID (无变化) | ### 防枚举设计 "无 CUSTOMIZER 角色 admin" 路径返回的 code/message 跟 "mobile 不绑 admin" 路径**完全一致** (200102 INVALID + remainingAttempts),攻击者无法区分这两种情况, 也无法用 SMS 入口枚举哪些 mobile 是非定制师 admin。 fail count 也会累加, 错 5 次仍触发 `ADMIN_SMS_CODE_VERIFY_LOCKED` 15 分钟锁定 (复用 #2889 锁定机制)。 ### active role 强制 CUSTOMIZER 后 - 颁发的 access token 内嵌的 `role` 字段 = `CUSTOMIZER` - 后续基于 token 的 `@PreAuthorize("hasRole('CUSTOMIZER')")` 等权限校验都按定制师视角 - **不污染 `admin.currentRoleId` 持久化**: SMS 是临时切角色, 不影响下次密码登录时的默认角色 --- ## 🚨 前端需要做的事 ### 1. 登录后角色显示 SMS 登录成功后 `data.role` 一定是 `CUSTOMIZER`, 顶部用户信息组件 / 角色 badge 应该按定制师展示。 不需要前端额外区分 SMS 登录 vs 账密登录: 后端已经把 role 强制设为 CUSTOMIZER, 前端按响应字段渲染即可。 ### 2. 多角色切换 如果未来前端有"切换角色"功能 (`PUT /admin/auth/role`), SMS 登录拿到的 CUSTOMIZER token, 切到其他角色行为等同于密码登录路径 (后端按现有逻辑处理)。 ### 3. 错码无新增 `200102` 错码已经存在, 前端无需新增映射。**`200107` 是后端审计码不会返前端**, 前端忽略。 ### 4. 自查 ```bash grep -rE "/admin/auth/sms/login|role.*CUSTOMIZER" hl-ui/src ``` 确认登录后 role 字段处理逻辑能正确显示 CUSTOMIZER。 --- ## 二、新错码 (后端审计用, 不返前端) | code | 含义 | |---|---| | 200107 | `ADMIN_SMS_LOGIN_NOT_CUSTOMIZER` 短信验证码登录仅限定制师 (仅服务端 log.warn 审计, 防枚举不返前端) | --- ## 三、测试服验证记录 部署到测试服 `api.test.1814.love:9443` 后真测 (2026-05-22 19:05): | # | 场景 | 实测 | |---|------|------| | T1 | 仅 CUSTOMIZER admin 登录 | ✅ `role=CUSTOMIZER` + `roleName=定制师` + token + `requireTwoFa=False` | | T2 | 仅 ADMIN admin (无 CUSTOMIZER) 登录 | ✅ 200102 INVALID + `remainingAttempts=4` (防枚举) | | T3 | 多角色 admin (ADMIN+CUSTOMIZER, currentRoleId=ADMIN) | ✅ 强制返 `role=CUSTOMIZER`, DB `current_role_id=2` 保持不污染 | --- ## 四、不在本期范围 - 账号密码登录路径 `/admin/auth/login` 行为完全不动 (仍按 `admin.currentRoleId`) - 让非定制师能用 SMS 登录 (明确仅定制师) - 持久化改 admin.currentRoleId (SMS 是临时切角色) --- ## 五、联系人 后端: wx (呼籁旅行) 前端: mmg 如有疑问可在 [#2918](https://git.1814.love:8443/wx/HL/issues/2918) 评论区留言。