diff --git a/docs/CurrentUserPasswordApi.md b/docs/CurrentUserPasswordApi.md new file mode 100644 index 0000000..d3c87dc --- /dev/null +++ b/docs/CurrentUserPasswordApi.md @@ -0,0 +1,127 @@ +# 当前登录账号修改密码接口说明 + +## 功能说明 + +- 适用范围:任意端口账号登录后修改当前登录账号自己的密码 +- 安全限制:只能修改当前登录态对应账号的密码,不能修改其他账号或其他端口账号的密码 +- 推荐接口:`PUT /system/user/profile/updatePwd` +- 兼容接口:`PUT /system/user/profile/updateSubPwd` + +## 规则说明 + +- 超级管理员修改的是 `sys_user.password` +- 普通端口账号修改的是当前登录端口记录 `sys_user_login_port.sub_password` +- 后端会从当前登录 token 中自动识别当前账号 +- 前端不需要传目标账号 ID +- 如果传了 `portId`,且与当前登录端口不一致,后端会直接返回失败:`只能修改当前登录账号密码` + +## 1. 推荐接口 + +- 路径:`PUT /system/user/profile/updatePwd` +- 说明:当前登录用户修改自己的密码 + +### 请求头 + +| 请求头 | 是否必填 | 说明 | +| --- | --- | --- | +| `Authorization` | 是 | 登录 token | +| `isEncrypt` | 是 | 传 `true`,与现有加密请求保持一致 | +| `repeatSubmit` | 否 | 可传 `false` | +| `Content-Type` | 是 | `application/json` | + +### 请求参数 + +| 字段 | 类型 | 必填 | 说明 | +| --- | --- | --- | --- | +| `oldPassword` | String | 是 | 旧密码 | +| `newPassword` | String | 是 | 新密码 | +| `portId` | Long | 否 | 非必填;建议不要传,若传入的不是当前登录端口 ID 会被拒绝 | + +### 请求示例 + +```json +{ + "oldPassword": "123456", + "newPassword": "NewPwd@2026" +} +``` + +### 返回成功示例 + +```json +{ + "code": 200, + "msg": "操作成功", + "data": null +} +``` + +### 常见失败响应 + +| 提示 | 说明 | +| --- | --- | +| `修改密码失败,旧密码错误` | 旧密码校验失败 | +| `新密码不能与旧密码相同` | 新旧密码一致 | +| `只能修改当前登录账号密码` | 试图修改非当前登录端口账号密码 | +| `当前登录账号未绑定端口,无法修改密码` | 当前登录态无端口信息 | +| `当前登录账号不存在或无权修改` | 当前登录端口记录不存在或不属于当前用户 | +| `修改密码异常,请联系管理员` | 数据更新失败 | + +## 2. 兼容接口 + +- 路径:`PUT /system/user/profile/updateSubPwd` +- 说明:为兼容历史前端保留,实际安全限制与推荐接口一致,也只能修改当前登录账号密码 + +### 请求方式 + +- `body` 仍传: + +```json +{ + "oldPassword": "123456", + "newPassword": "NewPwd@2026" +} +``` + +- 历史前端若在 query 中传 `portId`,只有在它等于当前登录端口 ID 时才允许通过 + +## 3. 前端调用示例 + +### Web 端 + +```ts +request({ + url: '/system/user/profile/updatePwd', + method: 'put', + headers: { + isEncrypt: true, + repeatSubmit: false + }, + data: { + oldPassword, + newPassword + } +}) +``` + +### uni-app / 移动端 + +```js +request({ + url: '/system/user/profile/updatePwd', + method: 'put', + header: { + isEncrypt: true + }, + data: { + oldPassword, + newPassword + } +}) +``` + +## 4. 对接建议 + +- 新接入前端统一调用 `PUT /system/user/profile/updatePwd` +- 不要让前端选择目标账号,也不要透传其他账号或其他端口的 `portId` +- 修改成功后,建议前端提示用户重新使用新密码登录 diff --git a/src/main/java/org/dromara/system/controller/system/SysProfileController.java b/src/main/java/org/dromara/system/controller/system/SysProfileController.java index a1793c0..3494be8 100644 --- a/src/main/java/org/dromara/system/controller/system/SysProfileController.java +++ b/src/main/java/org/dromara/system/controller/system/SysProfileController.java @@ -148,35 +148,10 @@ public class SysProfileController extends BaseController { @PutMapping("/updatePwd") public R updatePwd(@Validated @RequestBody SysUserPasswordBo bo) { Long userId = LoginHelper.getUserId(); - // 如果是超级管理员,走原来的密码修改逻辑 if (LoginHelper.isSuperAdmin(userId)) { - SysUserVo user = userService.selectUserById(userId); - String password = user.getPassword(); - if (!BCrypt.checkpw(bo.getOldPassword(), password)) { - return R.fail("修改密码失败,旧密码错误"); - } - if (BCrypt.checkpw(bo.getNewPassword(), password)) { - return R.fail("新密码不能与旧密码相同"); - } - int rows = DataPermissionHelper.ignore(() -> userService.resetUserPwd(user.getUserId(), BCrypt.hashpw(bo.getNewPassword()), null)); - if (rows > 0) { - return R.ok(); - } - return R.fail("修改密码异常,请联系管理员"); + return updateAdminPwd(bo, userId); } - - // 普通用户,修改当前登录端口的子密码 - // 优先使用前端传来的 portId - Long targetPortId = bo.getPortId(); - if (targetPortId == null) { - targetPortId = LoginHelper.getLoginUser().getCompanyRoleId(); - } - - if (targetPortId == null) { - return R.fail("无法确定修改的目标企业,请指定portId"); - } - - return updateSubPwd(bo, targetPortId); + return updateCurrentLoginPortPwd(bo, bo.getPortId()); } /** @@ -190,29 +165,43 @@ public class SysProfileController extends BaseController { @Log(title = "个人信息", businessType = BusinessType.UPDATE) @PutMapping("/updateSubPwd") public R updateSubPwd(@Validated @RequestBody SysUserPasswordBo bo, @RequestParam(required = false) Long portId) { + Long requestedPortId = portId != null ? portId : bo.getPortId(); + return updateCurrentLoginPortPwd(bo, requestedPortId); + } + + private R updateAdminPwd(SysUserPasswordBo bo, Long userId) { + SysUserVo user = userService.selectUserById(userId); + String password = user.getPassword(); + if (!BCrypt.checkpw(bo.getOldPassword(), password)) { + return R.fail("修改密码失败,旧密码错误"); + } + if (BCrypt.checkpw(bo.getNewPassword(), password)) { + return R.fail("新密码不能与旧密码相同"); + } + int rows = DataPermissionHelper.ignore(() -> userService.resetUserPwd(user.getUserId(), BCrypt.hashpw(bo.getNewPassword()), null)); + if (rows > 0) { + return R.ok(); + } + return R.fail("修改密码异常,请联系管理员"); + } + + private R updateCurrentLoginPortPwd(SysUserPasswordBo bo, Long requestedPortId) { Long userId = LoginHelper.getUserId(); - Long targetPortId = portId; - if (targetPortId == null) { - targetPortId = LoginHelper.getLoginUser().getCompanyRoleId(); + if (LoginHelper.getLoginUser() == null || LoginHelper.getLoginUser().getCompanyRoleId() == null) { + return R.fail("当前登录账号未绑定端口,无法修改密码"); } - if (targetPortId == null) { - return R.fail("无法确定修改的目标企业,请指定portId"); + Long currentPortId = LoginHelper.getLoginUser().getCompanyRoleId(); + if (requestedPortId != null && !requestedPortId.equals(currentPortId)) { + return R.fail("只能修改当前登录账号密码"); } - // Fetch port - SysUserLoginPort port = sysUserLoginPortService.getById(targetPortId); - if (port == null || !port.getUserId().equals(userId)) { - return R.fail("未找到对应企业端口或无权修改"); + SysUserLoginPort currentPort = sysUserLoginPortService.getById(currentPortId); + if (currentPort == null || !userId.equals(currentPort.getUserId())) { + return R.fail("当前登录账号不存在或无权修改"); } - // Check old password - String currentSubPwd = port.getSubPassword(); + String currentSubPwd = currentPort.getSubPassword(); if (StringUtils.isBlank(currentSubPwd)) { - // 如果没有设置子密码,视为未设置密码或无法修改(需根据业务决定,这里假设必须先有子密码) - // 或者,如果为空,允许直接设置? - // 考虑到用户迁移,如果为空,可能允许使用空密码验证?或者要求联系管理员重置。 - // 这里遵循用户指令:完全依赖子密码。如果为空,可能无法通过旧密码验证。 - // 但为了首次设置,如果为空,允许旧密码为空? if (StringUtils.isNotBlank(bo.getOldPassword())) { return R.fail("修改密码失败,旧密码错误"); } @@ -220,17 +209,16 @@ public class SysProfileController extends BaseController { if (!BCrypt.checkpw(bo.getOldPassword(), currentSubPwd)) { return R.fail("修改密码失败,旧密码错误"); } + if (BCrypt.checkpw(bo.getNewPassword(), currentSubPwd)) { + return R.fail("新密码不能与旧密码相同"); + } } - if (BCrypt.checkpw(bo.getNewPassword(), currentSubPwd)) { - return R.fail("新密码不能与旧密码相同"); + currentPort.setSubPassword(BCrypt.hashpw(bo.getNewPassword())); + if (sysUserLoginPortService.updateById(currentPort)) { + return R.ok(); } - - // Update - port.setSubPassword(BCrypt.hashpw(bo.getNewPassword())); - sysUserLoginPortService.updateById(port); - - return R.ok(); + return R.fail("修改密码异常,请联系管理员"); } /**