feat: 修改密码功能
This commit is contained in:
parent
3a8b70495a
commit
ee3ce43c8e
367
docs/superpowers/plans/2026-04-28-修改密码功能实现计划.md
Normal file
367
docs/superpowers/plans/2026-04-28-修改密码功能实现计划.md
Normal file
@ -0,0 +1,367 @@
|
|||||||
|
# 修改密码功能实现计划
|
||||||
|
|
||||||
|
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||||
|
|
||||||
|
**Goal:** 为碳信网平台添加普通用户自助修改密码功能(txw-sso后端 + txw-mhzc-web前端)
|
||||||
|
|
||||||
|
**Architecture:**
|
||||||
|
- 后端:SSO服务新增 `POST /sso/auth/changePassword` 接口,用户通过 authenticate 验证旧密码,通过 mhzc-service 的 Feign 接口更新用户密码
|
||||||
|
- 前端:mhzc-web 新增修改密码页面(账号管理),工作台增加快捷入口,用户中心左侧菜单增加"账号管理"菜单项
|
||||||
|
|
||||||
|
**Tech Stack:** Java Spring Boot / Vue 2 / TDesign / Feign
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 涉及的变更文件
|
||||||
|
|
||||||
|
### 后端(txw-sso)
|
||||||
|
| 文件 | 改动 |
|
||||||
|
|------|------|
|
||||||
|
| `AuthService.java` | 新增 `changePassword` 接口声明 |
|
||||||
|
| `AuthServiceImpl.java` | 实现 `changePassword` 逻辑 |
|
||||||
|
| `AuthController.java` | 新增 `/auth/changePassword` 路由 |
|
||||||
|
| `ChangePasswordReqVO.java` | 新建请求 VO |
|
||||||
|
| `ChangePasswordRespVO.java` | 新建响应 VO |
|
||||||
|
| `YhxxApi.java` (txw-sso-api) | 新增 Feign 方法 `updatePassword` |
|
||||||
|
| `TxwMhzcYhxxbService.java` (txw-mhzc) | 新增 `updatePassword` 方法声明 |
|
||||||
|
| `TxwMhzcYhxxbServiceImpl.java` (txw-mhzc) | 实现 `updatePassword` |
|
||||||
|
| `TxwMhzcYhxxbMapper.xml` | 新增 updatePassword SQL |
|
||||||
|
| `ISsoApi.java` | FeignClient name 常量(需确认mhzc服务名) |
|
||||||
|
|
||||||
|
### 前端(txw-mhzc-web)
|
||||||
|
| 文件 | 改动 |
|
||||||
|
|------|------|
|
||||||
|
| `views/gzt/index.vue` | 新增修改密码快捷入口按钮 |
|
||||||
|
| `views/yhzx/zhanghugl/index.vue` | 新建修改密码页面 |
|
||||||
|
| `router/routes.js` | 新增 `zhanghugl` 路由 |
|
||||||
|
| `views/glxtSy/config.js` | 菜单配置新增"账号管理" |
|
||||||
|
| `api/sso.js` | 新建,封装 changePassword API |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 后端任务拆分
|
||||||
|
|
||||||
|
### Task 1: 后端 - 新建 ChangePasswordReqVO
|
||||||
|
|
||||||
|
**文件:** `txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/vo/ChangePasswordReqVO.java`
|
||||||
|
|
||||||
|
- [ ] **Step 1: 创建文件**
|
||||||
|
|
||||||
|
```java
|
||||||
|
package com.css.txw.sso.pojo.vo;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotBlank;
|
||||||
|
|
||||||
|
@Schema(description = "修改密码 Request VO")
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Builder
|
||||||
|
public class ChangePasswordReqVO {
|
||||||
|
|
||||||
|
@Schema(description = "旧密码(MD5加密)", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
|
@NotBlank(message = "旧密码不能为空")
|
||||||
|
private String oldPassword;
|
||||||
|
|
||||||
|
@Schema(description = "新密码(MD5加密)", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
|
@NotBlank(message = "新密码不能为空")
|
||||||
|
private String newPassword;
|
||||||
|
|
||||||
|
@Schema(description = "确认密码(MD5加密)", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
|
@NotBlank(message = "确认密码不能为空")
|
||||||
|
private String confirmPassword;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 2: 后端 - AuthService 新增 changePassword 接口声明
|
||||||
|
|
||||||
|
**文件:** `txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/auth/AuthService.java`
|
||||||
|
|
||||||
|
在接口末尾添加:
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* 修改密码
|
||||||
|
*
|
||||||
|
* @param reqVO 修改密码请求
|
||||||
|
* @return 成功返回 null
|
||||||
|
*/
|
||||||
|
void changePassword(ChangePasswordReqVO reqVO);
|
||||||
|
```
|
||||||
|
|
||||||
|
引入 import:
|
||||||
|
```java
|
||||||
|
import com.css.txw.sso.pojo.vo.ChangePasswordReqVO;
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 3: 后端 - AuthServiceImpl 实现 changePassword
|
||||||
|
|
||||||
|
**文件:** `txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/auth/impl/AuthServiceImpl.java`
|
||||||
|
|
||||||
|
在类末尾添加实现方法:
|
||||||
|
|
||||||
|
```java
|
||||||
|
@Override
|
||||||
|
public void changePassword(ChangePasswordReqVO reqVO) {
|
||||||
|
// 1. 校验新密码与确认密码一致性
|
||||||
|
if (!reqVO.getNewPassword().equals(reqVO.getConfirmPassword())) {
|
||||||
|
throw exception(AUTH_PASSWORD_CONFIRM_MISMATCH);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 密码复杂度校验(6-20位,字母+数字)
|
||||||
|
if (!isValidPasswordComplexity(reqVO.getNewPassword())) {
|
||||||
|
throw exception(AUTH_PASSWORD_COMPLEXITY_INVALID);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 获取当前登录用户
|
||||||
|
String userId = SecurityFrameworkUtils.getLoginUserId();
|
||||||
|
YhxxbDTO yhxx = yhxxService.getYhxx(userId);
|
||||||
|
|
||||||
|
// 4. 校验新旧密码不能相同
|
||||||
|
if (reqVO.getOldPassword().equals(reqVO.getNewPassword())) {
|
||||||
|
throw exception(AUTH_PASSWORD_SAME_AS_OLD);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. 旧密码正确性校验
|
||||||
|
authenticate(yhxx.getDlzh(), reqVO.getOldPassword());
|
||||||
|
|
||||||
|
// 6. 更新密码
|
||||||
|
yhxxService.updatePassword(userId, reqVO.getNewPassword());
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isValidPasswordComplexity(String password) {
|
||||||
|
if (password == null || password.length() < 6 || password.length() > 20) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
boolean hasLetter = password.matches(".*[A-Za-z]+.*");
|
||||||
|
boolean hasDigit = password.matches(".*[0-9]+.*");
|
||||||
|
return hasLetter && hasDigit;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
新增引入:
|
||||||
|
```java
|
||||||
|
import com.css.txw.sso.pojo.vo.ChangePasswordReqVO;
|
||||||
|
import com.css.txw.sso.util.SecurityFrameworkUtils;
|
||||||
|
import static com.css.txw.sso.constants.ErrorCodeConstants.AUTH_PASSWORD_CONFIRM_MISMATCH;
|
||||||
|
import static com.css.txw.sso.constants.ErrorCodeConstants.AUTH_PASSWORD_COMPLEXITY_INVALID;
|
||||||
|
import static com.css.txw.sso.constants.ErrorCodeConstants.AUTH_PASSWORD_SAME_AS_OLD;
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 4: 后端 - ErrorCodeConstants 新增错误码
|
||||||
|
|
||||||
|
**文件:** `txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/constants/ErrorCodeConstants.java`
|
||||||
|
|
||||||
|
新增三个错误码:
|
||||||
|
```java
|
||||||
|
AUTH_PASSWORD_CONFIRM_MISMATCH(400, "两次输入的新密码不一致"),
|
||||||
|
AUTH_PASSWORD_COMPLEXITY_INVALID(400, "密码长度6-20位,需包含字母和数字"),
|
||||||
|
AUTH_PASSWORD_SAME_AS_OLD(400, "新密码不能与原密码相同"),
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 5: 后端 - AuthController 新增路由
|
||||||
|
|
||||||
|
**文件:** `txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/controller/auth/AuthController.java`
|
||||||
|
|
||||||
|
在类末尾添加:
|
||||||
|
|
||||||
|
```java
|
||||||
|
@PostMapping("/changePassword")
|
||||||
|
@Operation(summary = "修改密码")
|
||||||
|
public CommonResult<Void> changePassword(@RequestBody @Valid ChangePasswordReqVO reqVO) {
|
||||||
|
authService.changePassword(reqVO);
|
||||||
|
return success(null);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
新增引入:
|
||||||
|
```java
|
||||||
|
import com.css.txw.sso.pojo.vo.ChangePasswordReqVO;
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 6: 后端 - YhxxService 新增 updatePassword 方法
|
||||||
|
|
||||||
|
**文件:** `txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/yhxx/YhxxService.java`
|
||||||
|
|
||||||
|
末尾新增方法声明:
|
||||||
|
```java
|
||||||
|
void updatePassword(String yhUuid, String newPassword);
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 7: 后端 - YhxxServiceImpl 实现 updatePassword(Feign调用mhzc)
|
||||||
|
|
||||||
|
**文件:** `txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/yhxx/impl/YhxxServiceImpl.java`
|
||||||
|
|
||||||
|
首先需要确认 mhzc 服务的 Feign 接口存在。如果 YhxxApi 不支持更新密码,则需要在 txw-sso-api 中新增 Feign 方法指向 mhzc 的更新密码接口。
|
||||||
|
|
||||||
|
步骤:
|
||||||
|
1. 在 `txw-sso-api` 的 `YhxxApi.java` 中新增 `updatePassword` Feign 方法
|
||||||
|
2. 在 `txw-mhzc` 的 `TxwMhzcYhxxbService` 新增 `updatePassword` 方法
|
||||||
|
3. 在 `TxwMhzcYhxxbServiceImpl` 实现该方法
|
||||||
|
4. 在 mapper XML 中新增 update SQL
|
||||||
|
5. 在 `YhxxServiceImpl` 中调用该 Feign 接口
|
||||||
|
|
||||||
|
**YhxxApi.java (txw-sso-api) 新增:**
|
||||||
|
```java
|
||||||
|
@PostMapping("/mhzc/user/updatePassword")
|
||||||
|
@Operation(summary = "更新用户密码")
|
||||||
|
CommonResult<String> updatePassword(@RequestParam("yhUuid") String yhUuid, @RequestParam("dlmm") String dlmm);
|
||||||
|
```
|
||||||
|
|
||||||
|
**TxwMhzcYhxxbService.java 新增:**
|
||||||
|
```java
|
||||||
|
String updatePassword(String yhUuid, String dlmm);
|
||||||
|
```
|
||||||
|
|
||||||
|
**TxwMhzcYhxxbServiceImpl.java 新增:**
|
||||||
|
```java
|
||||||
|
@Override
|
||||||
|
public String updatePassword(String yhUuid, String dlmm) {
|
||||||
|
TxwMhzcYhxxbDO yhxxbDO = new TxwMhzcYhxxbDO();
|
||||||
|
yhxxbDO.setYhUuid(yhUuid);
|
||||||
|
yhxxbDO.setDlmm(dlmm);
|
||||||
|
this.updateById(yhxxbDO);
|
||||||
|
return "success";
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**TxwMhzcYhxxbMapper.xml 新增:**
|
||||||
|
```xml
|
||||||
|
<update id="updatePassword">
|
||||||
|
UPDATE txw_mhzc_yhxxb SET dlmm = #{dlmm} WHERE yh_uuid = #{yhUuid}
|
||||||
|
</update>
|
||||||
|
```
|
||||||
|
|
||||||
|
**YhxxServiceImpl.java 实现:**
|
||||||
|
```java
|
||||||
|
@Resource
|
||||||
|
private YhxxApi yhxxApi;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updatePassword(String yhUuid, String newPassword) {
|
||||||
|
yhxxApi.updatePassword(yhUuid, newPassword);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 前端任务拆分(txw-mhzc-web)
|
||||||
|
|
||||||
|
### Task 8: 前端 - 新建 api/sso.js
|
||||||
|
|
||||||
|
**文件:** `txw-mhzc-web/src/pages/index/api/sso.js`
|
||||||
|
|
||||||
|
```js
|
||||||
|
import { fetchSso } from '@/core/request';
|
||||||
|
|
||||||
|
export const changePassword = (params) => {
|
||||||
|
return fetchSso({
|
||||||
|
url: '/sso/auth/changePassword',
|
||||||
|
data: JSON.stringify(params),
|
||||||
|
method: 'post',
|
||||||
|
loading: true,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 9: 前端 - 新建修改密码页面
|
||||||
|
|
||||||
|
**文件:** `txw-mhzc-web/src/pages/index/views/yhzx/zhanghugl/index.vue`
|
||||||
|
|
||||||
|
页面元素:
|
||||||
|
- 三个密码输入框(旧密码、新密码、确认密码)
|
||||||
|
- 确认按钮
|
||||||
|
- 密码格式提示文字
|
||||||
|
|
||||||
|
校验逻辑:
|
||||||
|
1. 旧密码、新密码、确认密码不能为空
|
||||||
|
2. 新密码长度6-20位,需包含字母和数字(提示:密码长度6-20位,需包含字母和数字)
|
||||||
|
3. 新密码与确认密码一致性(提示:两次输入的新密码不一致)
|
||||||
|
4. 提交后调用 changePassword API
|
||||||
|
5. 成功提示"密码修改成功",可跳转回工作台
|
||||||
|
|
||||||
|
样式参考现有的登录页面,使用 TDesign 组件库。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 10: 前端 - 路由配置新增 zhanghugl
|
||||||
|
|
||||||
|
**文件:** `txw-mhzc-web/src/pages/index/router/routes.js`
|
||||||
|
|
||||||
|
在 `yhzx` 的 children 数组中新增:
|
||||||
|
```js
|
||||||
|
{ path: 'zhanghugl', component: zhanghugl, name: 'zhanghugl', meta: { title: '账号管理' } }
|
||||||
|
```
|
||||||
|
|
||||||
|
新增 zhanghugl 路由函数:
|
||||||
|
```js
|
||||||
|
function zhanghugl() {
|
||||||
|
return import('@/pages/index/views/yhzx/zhanghugl/index.vue');
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 11: 前端 - 菜单配置新增"账号管理"
|
||||||
|
|
||||||
|
**文件:** `txw-mhzc-web/src/pages/index/views/glxtSy/config.js`
|
||||||
|
|
||||||
|
在 `menuList` 数组中新增一项:
|
||||||
|
```js
|
||||||
|
{
|
||||||
|
value: 'zhanghugl',
|
||||||
|
title: '账号管理',
|
||||||
|
icon: 'password',
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 12: 前端 - 工作台新增修改密码快捷入口
|
||||||
|
|
||||||
|
**文件:** `txw-mhzc-web/src/pages/index/views/gzt/index.vue`
|
||||||
|
|
||||||
|
在快捷操作区域新增按钮:
|
||||||
|
```html
|
||||||
|
<t-button theme="primary" variant="outline" @click="$router.push('/yhzx/zhanghugl')">
|
||||||
|
修改密码
|
||||||
|
</t-button>
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 任务执行顺序
|
||||||
|
|
||||||
|
建议按以下顺序执行:
|
||||||
|
|
||||||
|
1. Task 1 - 新建 ChangePasswordReqVO
|
||||||
|
2. Task 4 - 新增错误码
|
||||||
|
3. Task 6 - YhxxService 新增 updatePassword
|
||||||
|
4. Task 7 - YhxxServiceImpl + Feign + MHZC 实现
|
||||||
|
5. Task 2 - AuthService 新增 changePassword
|
||||||
|
6. Task 3 - AuthServiceImpl 实现
|
||||||
|
7. Task 5 - AuthController 新增路由
|
||||||
|
8. Task 8 - 前端 API 新建
|
||||||
|
9. Task 9 - 前端页面新建
|
||||||
|
10. Task 10 - 路由配置
|
||||||
|
11. Task 11 - 菜单配置
|
||||||
|
12. Task 12 - 工作台入口
|
||||||
147
docs/superpowers/specs/2026-04-28-修改密码功能设计.md
Normal file
147
docs/superpowers/specs/2026-04-28-修改密码功能设计.md
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
# 修改密码功能设计
|
||||||
|
|
||||||
|
## 1. 概述
|
||||||
|
|
||||||
|
为碳信网平台添加普通用户自助修改密码功能,采用前后端分离架构。
|
||||||
|
|
||||||
|
- **普通用户修改密码**:位于 `txw-mhzc`(用户中心),入口在个人中心左侧菜单
|
||||||
|
- **管理员重置密码**:已存在于 `txw-yygl`,无需改动
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. 后端接口
|
||||||
|
|
||||||
|
### 2.1 新增接口
|
||||||
|
|
||||||
|
**POST `/sso/auth/changePassword`**
|
||||||
|
|
||||||
|
请求体:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"oldPassword": "MD5加密字符串",
|
||||||
|
"newPassword": "MD5加密字符串",
|
||||||
|
"confirmPassword": "MD5加密字符串"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
响应(统一 `CommonResult`):
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 1,
|
||||||
|
"data": null,
|
||||||
|
"msg": "密码修改成功"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.2 密码复杂度规则
|
||||||
|
|
||||||
|
**中等级别**:
|
||||||
|
- 长度:6-20 位
|
||||||
|
- 必须包含:字母 + 数字
|
||||||
|
|
||||||
|
### 2.3 校验流程
|
||||||
|
|
||||||
|
1. **格式校验**:新密码长度6-20位,需同时包含字母和数字
|
||||||
|
2. **一致性校验**:newPassword === confirmPassword
|
||||||
|
3. **身份校验**:authenticate(username, oldPassword) 验证旧密码正确性
|
||||||
|
4. **防重名校验**:newPassword !== oldPassword
|
||||||
|
|
||||||
|
### 2.4 错误码
|
||||||
|
|
||||||
|
| 场景 | msg |
|
||||||
|
|------|-----|
|
||||||
|
| 密码格式不符 | 密码长度6-20位,需包含字母和数字 |
|
||||||
|
| 两次密码不一致 | 两次输入的新密码不一致 |
|
||||||
|
| 旧密码错误 | 原密码输入错误 |
|
||||||
|
| 新旧密码相同 | 新密码不能与原密码相同 |
|
||||||
|
|
||||||
|
### 2.5 涉及文件
|
||||||
|
|
||||||
|
| 文件 | 改动 |
|
||||||
|
|------|------|
|
||||||
|
| `AuthService.java` | 新增 `changePassword` 接口声明 |
|
||||||
|
| `AuthServiceImpl.java` | 实现 `changePassword` 逻辑 |
|
||||||
|
| `AuthController.java` | 新增 `/auth/changePassword` 路由 |
|
||||||
|
| `AuthLoginReqVO.java` | 建议复用或新建 `ChangePasswordReqVO` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. 前端页面(txw-mhzc-web)
|
||||||
|
|
||||||
|
### 3.1 路由
|
||||||
|
|
||||||
|
| 路由路径 | 组件 | 菜单标题 |
|
||||||
|
|---------|------|---------|
|
||||||
|
| `/yhzx/zhanghugl` | `ChangePassword.vue` | 账号管理 |
|
||||||
|
|
||||||
|
### 3.2 工作台入口
|
||||||
|
|
||||||
|
在 `txw-mhzc-web/src/pages/index/views/gzt/index.vue` 新增快捷操作按钮"修改密码",点击跳转 `/yhzx/zhanghugl`。
|
||||||
|
|
||||||
|
### 3.3 用户中心左侧菜单
|
||||||
|
|
||||||
|
在 `yhzx` 路由的 children 中新增:
|
||||||
|
```js
|
||||||
|
{ path: 'zhanghugl', component: zhanghugl, name: 'zhanghugl', meta: { title: '账号管理' } }
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.4 修改密码页面 `ChangePassword.vue`
|
||||||
|
|
||||||
|
**页面元素**:
|
||||||
|
- 旧密码输入框(密码类型)
|
||||||
|
- 新密码输入框(密码类型)
|
||||||
|
- 确认密码输入框(密码类型)
|
||||||
|
- 确认按钮
|
||||||
|
|
||||||
|
**交互逻辑**:
|
||||||
|
- 输入框下方实时显示密码格式提示
|
||||||
|
- 提交前统一校验
|
||||||
|
- 成功后给出提示并可跳转其他页面
|
||||||
|
- 失败回显错误信息
|
||||||
|
|
||||||
|
### 3.5 API 调用
|
||||||
|
|
||||||
|
```js
|
||||||
|
// txw-mhzc-web/src/pages/index/api/sso.js
|
||||||
|
export const changePassword = (params) => {
|
||||||
|
return fetchSso({
|
||||||
|
url: '/sso/auth/changePassword',
|
||||||
|
data: JSON.stringify(params),
|
||||||
|
method: 'post',
|
||||||
|
loading: true,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.6 涉及文件
|
||||||
|
|
||||||
|
| 文件 | 改动 |
|
||||||
|
|------|------|
|
||||||
|
| `router/routes.js` | 新增 `zhanghugl` 路由 |
|
||||||
|
| `views/gzt/index.vue` | 新增修改密码快捷入口 |
|
||||||
|
| `views/yhzx/zhanghugl/index.vue` | 新建修改密码页面 |
|
||||||
|
| `api/sso.js` | 新建,封装 changePassword API |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. yygl 管理员重置密码(已有,无需改动)
|
||||||
|
|
||||||
|
| 文件 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| `txw-yygl-web/.../yhgl/index.vue` | "重置密码"按钮,调用 `resetPassword(yhUuid)` |
|
||||||
|
| `txw-yygl-web/.../api/htgl.js` | `resetPassword` 接口已实现 |
|
||||||
|
| `txw-mhzc/.../UserController.java` | `resetPassword` 接口已实现,重置为配置默认密码 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. 数据库字段
|
||||||
|
|
||||||
|
用户表 `dlmm` 字段存储 MD5 加密后的密码。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. 备注
|
||||||
|
|
||||||
|
- 旧密码传输前已在前端做 MD5 加密(与现有登录流程一致)
|
||||||
|
- 新密码传输前在前端做 MD5 加密
|
||||||
|
- 此设计与现有 `authenticate` 的 `isPasswordMatch` 逻辑保持一致
|
||||||
@ -107,6 +107,16 @@ export function mhLogout() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 修改密码(用户自助)
|
||||||
|
export function changePassword(params) {
|
||||||
|
return fetchSso({
|
||||||
|
headers,
|
||||||
|
url: `${basurl}/sso/auth/changePassword`,
|
||||||
|
data: JSON.stringify(params),
|
||||||
|
method: 'post',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// 获取重定向地址
|
// 获取重定向地址
|
||||||
export function getRedirectUri() {
|
export function getRedirectUri() {
|
||||||
return fetchSso({
|
return fetchSso({
|
||||||
|
|||||||
@ -101,6 +101,11 @@ function search() {
|
|||||||
return import(/* webpackChunkName: "search" */ '@/pages/index/views/search/index.vue');
|
return import(/* webpackChunkName: "search" */ '@/pages/index/views/search/index.vue');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 账号管理
|
||||||
|
function zhanghugl() {
|
||||||
|
return import(/* webpackChunkName: "zhanghugl" */ '@/pages/index/views/yhzx/zhanghugl/index.vue');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -177,6 +182,7 @@ export default [
|
|||||||
{ path: 'tfwxq', component: tfwxq, name: 'tfwxq', meta: { title: '碳服务需求' } },
|
{ path: 'tfwxq', component: tfwxq, name: 'tfwxq', meta: { title: '碳服务需求' } },
|
||||||
{ path: 'ggwhgl', component: newsCenter, name: 'ggwhgl', meta: { title: '消息中心' } },
|
{ path: 'ggwhgl', component: newsCenter, name: 'ggwhgl', meta: { title: '消息中心' } },
|
||||||
{ path: 'lsjy', component: lsjy, name: 'lsjy', meta: { title: '绿色交易' } },
|
{ path: 'lsjy', component: lsjy, name: 'lsjy', meta: { title: '绿色交易' } },
|
||||||
|
{ path: 'zhanghugl', component: zhanghugl, name: 'zhanghugl', meta: { title: '账号管理' } },
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@ -24,4 +24,9 @@ export const menuList = [
|
|||||||
icon: 'circle',
|
icon: 'circle',
|
||||||
children: [],
|
children: [],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
value: 'zhanghugl',
|
||||||
|
title: '账号管理',
|
||||||
|
icon: 'password',
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@ -45,7 +45,7 @@ export default {
|
|||||||
{ label: '消息中心', icon: 'mail', to: '/yhzx/ggwhgl', bgColor: '#FCE4EC', color: '#E91E63' },
|
{ label: '消息中心', icon: 'mail', to: '/yhzx/ggwhgl', bgColor: '#FCE4EC', color: '#E91E63' },
|
||||||
{ label: '我的认证', icon: 'user', to: '/yhzx/qyrenzheng', bgColor: '#E3F2FD', color: '#2196F3' },
|
{ label: '我的认证', icon: 'user', to: '/yhzx/qyrenzheng', bgColor: '#E3F2FD', color: '#2196F3' },
|
||||||
{ label: '账号编辑', icon: 'edit', to: '/yhzx/qyrenzheng', bgColor: '#FFF8E1', color: '#FF9800' },
|
{ label: '账号编辑', icon: 'edit', to: '/yhzx/qyrenzheng', bgColor: '#FFF8E1', color: '#FF9800' },
|
||||||
{ label: '修改密码', icon: 'lock-on', to: '/yhzx/qyrenzheng', bgColor: '#E8F5E9', color: '#4CAF50' },
|
{ label: '修改密码', icon: 'lock-on', to: '/yhzx/zhanghugl', bgColor: '#E8F5E9', color: '#4CAF50' },
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|||||||
192
txw-mhzc-web/src/pages/index/views/yhzx/zhanghugl/index.vue
Normal file
192
txw-mhzc-web/src/pages/index/views/yhzx/zhanghugl/index.vue
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
<template>
|
||||||
|
<div class="change-password-page">
|
||||||
|
<div class="page-header">
|
||||||
|
<div class="page-title">账号管理</div>
|
||||||
|
</div>
|
||||||
|
<div class="page-content">
|
||||||
|
<t-form
|
||||||
|
ref="formRef"
|
||||||
|
:data="formData"
|
||||||
|
:rules="rules"
|
||||||
|
labelWidth="120"
|
||||||
|
@submit="onSubmit"
|
||||||
|
class="password-form"
|
||||||
|
>
|
||||||
|
<t-form-item label="旧密码" name="oldPassword">
|
||||||
|
<t-input
|
||||||
|
v-model="formData.oldPassword"
|
||||||
|
type="password"
|
||||||
|
placeholder="请输入旧密码"
|
||||||
|
clearable
|
||||||
|
showPassword
|
||||||
|
/>
|
||||||
|
</t-form-item>
|
||||||
|
|
||||||
|
<t-form-item label="新密码" name="newPassword">
|
||||||
|
<t-input
|
||||||
|
v-model="formData.newPassword"
|
||||||
|
type="password"
|
||||||
|
placeholder="请输入新密码"
|
||||||
|
clearable
|
||||||
|
showPassword
|
||||||
|
/>
|
||||||
|
<div class="password-tip">密码长度6-20位,需包含字母和数字</div>
|
||||||
|
</t-form-item>
|
||||||
|
|
||||||
|
<t-form-item label="确认密码" name="confirmPassword">
|
||||||
|
<t-input
|
||||||
|
v-model="formData.confirmPassword"
|
||||||
|
type="password"
|
||||||
|
placeholder="请再次输入新密码"
|
||||||
|
clearable
|
||||||
|
showPassword
|
||||||
|
/>
|
||||||
|
</t-form-item>
|
||||||
|
|
||||||
|
<t-form-item class="submit-item">
|
||||||
|
<t-button theme="primary" type="submit" :loading="submitLoading">确认修改</t-button>
|
||||||
|
<t-button theme="default" variant="outline" @click="onReset" style="margin-left: 12px">重置</t-button>
|
||||||
|
</t-form-item>
|
||||||
|
</t-form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { changePassword } from '@/pages/index/api/login';
|
||||||
|
import { MessagePlugin } from 'tdesign-vue';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'ChangePassword',
|
||||||
|
data() {
|
||||||
|
// 密码复杂度校验
|
||||||
|
const validatePasswordComplexity = (value) => {
|
||||||
|
if (!value || value.length < 6 || value.length > 20) {
|
||||||
|
return { valid: false, message: '密码长度6-20位' };
|
||||||
|
}
|
||||||
|
const hasLetter = /[A-Za-z]/.test(value);
|
||||||
|
const hasDigit = /[0-9]/.test(value);
|
||||||
|
if (!hasLetter || !hasDigit) {
|
||||||
|
return { valid: false, message: '需包含字母和数字' };
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 密码一致性校验
|
||||||
|
const validateConfirmPassword = (value) => {
|
||||||
|
if (value !== this.formData.newPassword) {
|
||||||
|
return { valid: false, message: '两次输入的新密码不一致' };
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
formData: {
|
||||||
|
oldPassword: '',
|
||||||
|
newPassword: '',
|
||||||
|
confirmPassword: '',
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
oldPassword: [
|
||||||
|
{ required: true, message: '旧密码不能为空', trigger: 'blur' },
|
||||||
|
],
|
||||||
|
newPassword: [
|
||||||
|
{ required: true, message: '新密码不能为空', trigger: 'blur' },
|
||||||
|
{
|
||||||
|
validator: validatePasswordComplexity,
|
||||||
|
message: '密码长度6-20位,需包含字母和数字',
|
||||||
|
trigger: 'blur',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
confirmPassword: [
|
||||||
|
{ required: true, message: '确认密码不能为空', trigger: 'blur' },
|
||||||
|
{
|
||||||
|
validator: validateConfirmPassword,
|
||||||
|
message: '两次输入的新密码不一致',
|
||||||
|
trigger: 'blur',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
submitLoading: false,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onSubmit({ validateResult }) {
|
||||||
|
if (validateResult !== true) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.submitLoading = true;
|
||||||
|
|
||||||
|
const params = {
|
||||||
|
oldPassword: this.formData.oldPassword,
|
||||||
|
newPassword: this.formData.newPassword,
|
||||||
|
confirmPassword: this.formData.confirmPassword,
|
||||||
|
};
|
||||||
|
|
||||||
|
changePassword(params)
|
||||||
|
.then((res) => {
|
||||||
|
if (res.code === 1) {
|
||||||
|
MessagePlugin.success('密码修改成功');
|
||||||
|
this.onReset();
|
||||||
|
setTimeout(() => {
|
||||||
|
this.$router.push('/yhzx/gzt');
|
||||||
|
}, 1500);
|
||||||
|
} else {
|
||||||
|
MessagePlugin.error(res.msg || '密码修改失败');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
MessagePlugin.error(err.msg || '密码修改失败');
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.submitLoading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onReset() {
|
||||||
|
this.formData = {
|
||||||
|
oldPassword: '',
|
||||||
|
newPassword: '',
|
||||||
|
confirmPassword: '',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.change-password-page {
|
||||||
|
padding: 24px;
|
||||||
|
background: #fff;
|
||||||
|
min-height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-header {
|
||||||
|
margin-bottom: 32px;
|
||||||
|
border-bottom: 1px solid #eee;
|
||||||
|
padding-bottom: 16px;
|
||||||
|
|
||||||
|
.page-title {
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #1a1b24;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-content {
|
||||||
|
max-width: 480px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.password-form {
|
||||||
|
.password-tip {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #999;
|
||||||
|
margin-top: 4px;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.submit-item {
|
||||||
|
margin-top: 24px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -64,4 +64,12 @@ public interface IMhzcApi {
|
|||||||
@Operation(summary = "更新用户did信息")
|
@Operation(summary = "更新用户did信息")
|
||||||
CommonResult<YhxxbDTO> updateDid(@RequestBody YhxxbDTO yhxx);
|
CommonResult<YhxxbDTO> updateDid(@RequestBody YhxxbDTO yhxx);
|
||||||
|
|
||||||
|
@PostMapping(PREFIX+"/updatePassword")
|
||||||
|
@Operation(summary = "更新用户密码")
|
||||||
|
CommonResult<String> updatePassword(@RequestBody YhxxReqDTO reqDTO);
|
||||||
|
|
||||||
|
@PostMapping(PREFIX+"/resetPassword")
|
||||||
|
@Operation(summary = "重置用户密码")
|
||||||
|
CommonResult<String> resetPassword(@RequestBody YhxxReqDTO reqDTO);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,4 +18,6 @@ public class YhxxReqDTO implements Serializable {
|
|||||||
private String scxzmx;
|
private String scxzmx;
|
||||||
private String did;
|
private String did;
|
||||||
private String sfzjhm;
|
private String sfzjhm;
|
||||||
|
private String dlmm;
|
||||||
|
private String oldPassword;
|
||||||
}
|
}
|
||||||
@ -113,10 +113,16 @@ public class UserController {
|
|||||||
return CommonResult.success(yhxxbService.getAllUserId());
|
return CommonResult.success(yhxxbService.getAllUserId());
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/resetPassword")
|
@PostMapping("/resetPassword")
|
||||||
@Operation(summary = "用户列表")
|
@Operation(summary = "重置密码")
|
||||||
CommonResult<String> resetPassword(@RequestParam("yhUuid") String yhuuid){
|
CommonResult<String> resetPassword(@RequestBody YhxxReqDTO reqDTO){
|
||||||
return CommonResult.success(yhxxbService.resetPassword(yhuuid));
|
return CommonResult.success(yhxxbService.resetPassword(reqDTO.getYhuuid(), reqDTO.getDlmm()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/updatePassword")
|
||||||
|
@Operation(summary = "更新密码")
|
||||||
|
CommonResult<String> updatePassword(@RequestBody YhxxReqDTO reqDTO){
|
||||||
|
return CommonResult.success(yhxxbService.updatePassword(reqDTO.getYhuuid(), reqDTO.getDlmm(), reqDTO.getOldPassword()));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,8 +27,6 @@ public interface TxwMhzcYhxxbService extends IService<TxwMhzcYhxxbDO> {
|
|||||||
|
|
||||||
Page<UserVO> getAllUser(UserReqVO reqVO);
|
Page<UserVO> getAllUser(UserReqVO reqVO);
|
||||||
|
|
||||||
String resetPassword(String yhuuid);
|
|
||||||
|
|
||||||
List<String> getAllUserId();
|
List<String> getAllUserId();
|
||||||
|
|
||||||
Integer lockUser(UserLockVO infoVO);
|
Integer lockUser(UserLockVO infoVO);
|
||||||
@ -42,4 +40,8 @@ public interface TxwMhzcYhxxbService extends IService<TxwMhzcYhxxbDO> {
|
|||||||
YhxxbDTO saveYhxxByDid(YhxxbDTO yhxx);
|
YhxxbDTO saveYhxxByDid(YhxxbDTO yhxx);
|
||||||
|
|
||||||
YhxxbDTO updateDid(YhxxbDTO yhxx);
|
YhxxbDTO updateDid(YhxxbDTO yhxx);
|
||||||
|
|
||||||
|
String updatePassword(String yhUuid, String dlmm, String oldPassword);
|
||||||
|
|
||||||
|
String resetPassword(String yhUuid, String dlmm);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -243,14 +243,28 @@ public class TxwMhzcYhxxbServiceImpl extends ServiceImpl<TxwMhzcYhxxbMapper, Txw
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String resetPassword(String yhuuid) {
|
public String resetPassword(String yhuuid, String dlmm) {
|
||||||
TxwMhzcYhxxbDO yhxxbDO = new TxwMhzcYhxxbDO();
|
TxwMhzcYhxxbDO yhxxbDO = new TxwMhzcYhxxbDO();
|
||||||
yhxxbDO.setYhUuid(yhuuid);
|
yhxxbDO.setYhUuid(yhuuid);
|
||||||
yhxxbDO.setDlmm(CacheUtils.dm2mc("cs_ggzc_xtcs", "dlmm"));
|
yhxxbDO.setDlmm(MD5.create().digestHex(dlmm));
|
||||||
this.updateById(yhxxbDO);
|
this.updateById(yhxxbDO);
|
||||||
return "success";
|
return "success";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String updatePassword(String yhUuid, String dlmm, String oldPassword) {
|
||||||
|
TxwMhzcYhxxbDO yhxxbDO = this.getById(yhUuid);
|
||||||
|
if (yhxxbDO == null) {
|
||||||
|
throw new RuntimeException("用户不存在");
|
||||||
|
}
|
||||||
|
// oldPassword已在SSO层校验过,dlmm是新密码明文,需MD5加密存储
|
||||||
|
TxwMhzcYhxxbDO updateDO = new TxwMhzcYhxxbDO();
|
||||||
|
updateDO.setYhUuid(yhUuid);
|
||||||
|
updateDO.setDlmm(MD5.create().digestHex(dlmm));
|
||||||
|
this.updateById(updateDO);
|
||||||
|
return "success";
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<String> getAllUserId() {
|
public List<String> getAllUserId() {
|
||||||
List<TxwMhzcYhxxbDO> allUserId = yhxxbMapper.getAllUserId();
|
List<TxwMhzcYhxxbDO> allUserId = yhxxbMapper.getAllUserId();
|
||||||
|
|||||||
@ -1,18 +0,0 @@
|
|||||||
package com.css.txw.sso.api.yhxx;
|
|
||||||
|
|
||||||
import com.css.ggzc.framework.common.pojo.CommonResult;
|
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
|
||||||
import org.springframework.cloud.openfeign.FeignClient;
|
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
|
||||||
|
|
||||||
@FeignClient(name = "sso-service")
|
|
||||||
@Tag(name = "用户信息接口")
|
|
||||||
public interface YhxxApi {
|
|
||||||
|
|
||||||
@PostMapping("/sso/yhxx/getYhxx")
|
|
||||||
@Operation(summary = "获取用户信息")
|
|
||||||
CommonResult<String> getYhxx(@RequestParam("djxh") String djxh);
|
|
||||||
|
|
||||||
}
|
|
||||||
Binary file not shown.
@ -41,5 +41,10 @@ public interface ErrorCodeConstants {
|
|||||||
ErrorCode OAUTH2_SJHM_LOCK = new ErrorCode(1004019, "距离上次发送验证码不超过1分钟");
|
ErrorCode OAUTH2_SJHM_LOCK = new ErrorCode(1004019, "距离上次发送验证码不超过1分钟");
|
||||||
ErrorCode OAUTH2_LOGIN_SMS_NOT_EXISTS = new ErrorCode(1004020, "验证码无效或已过期");
|
ErrorCode OAUTH2_LOGIN_SMS_NOT_EXISTS = new ErrorCode(1004020, "验证码无效或已过期");
|
||||||
|
|
||||||
|
// ========== 修改密码 1-002-030-000 ==========
|
||||||
|
ErrorCode AUTH_PASSWORD_CONFIRM_MISMATCH = new ErrorCode(1004021, "两次输入的新密码不一致");
|
||||||
|
ErrorCode AUTH_PASSWORD_COMPLEXITY_INVALID = new ErrorCode(1004022, "密码长度6-20位,需包含字母和数字");
|
||||||
|
ErrorCode AUTH_PASSWORD_SAME_AS_OLD = new ErrorCode(1004023, "新密码不能与原密码相同");
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,7 +10,7 @@ public class SsoConstants {
|
|||||||
|
|
||||||
public static final String CLIENT_DEFAULT = "default";
|
public static final String CLIENT_DEFAULT = "default";
|
||||||
|
|
||||||
public static final String SMS_CLIENT_SIGNNAME = "智贸链";
|
public static final String SMS_CLIENT_SIGNNAME = "可信碳信息网";
|
||||||
|
|
||||||
public static final String SMS_TEMPLATE_CODE = "SMS_474450289";
|
public static final String SMS_TEMPLATE_CODE = "SMS_474450289";
|
||||||
|
|
||||||
|
|||||||
@ -28,6 +28,7 @@ import com.css.txw.sso.constants.SsoConstants;
|
|||||||
import com.css.txw.sso.enums.LoginLogTypeEnum;
|
import com.css.txw.sso.enums.LoginLogTypeEnum;
|
||||||
import com.css.txw.sso.pojo.vo.AuthLoginReqVO;
|
import com.css.txw.sso.pojo.vo.AuthLoginReqVO;
|
||||||
import com.css.txw.sso.pojo.vo.AuthLoginRespVO;
|
import com.css.txw.sso.pojo.vo.AuthLoginRespVO;
|
||||||
|
import com.css.txw.sso.pojo.vo.ChangePasswordReqVO;
|
||||||
import com.css.txw.sso.pojo.vo.DidBindPhoneReqVO;
|
import com.css.txw.sso.pojo.vo.DidBindPhoneReqVO;
|
||||||
import com.css.txw.sso.pojo.vo.SMSLoginReqVO;
|
import com.css.txw.sso.pojo.vo.SMSLoginReqVO;
|
||||||
import com.css.txw.sso.pojo.vo.SendMsgReqVO;
|
import com.css.txw.sso.pojo.vo.SendMsgReqVO;
|
||||||
@ -144,4 +145,19 @@ public class AuthController {
|
|||||||
response.addCookie(cookie);
|
response.addCookie(cookie);
|
||||||
return success(login);
|
return success(login);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PostMapping("/changePassword")
|
||||||
|
@Operation(summary = "修改密码")
|
||||||
|
public CommonResult<Void> changePassword(HttpServletRequest request, @RequestBody @Valid ChangePasswordReqVO reqVO) {
|
||||||
|
String token = SecurityFrameworkUtils.obtainAuthorization(SsoConstants.COOKIE_TOKEN_KEY, request);
|
||||||
|
authService.changePassword(token, reqVO);
|
||||||
|
return success(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/resetPassword")
|
||||||
|
@Operation(summary = "重置密码")
|
||||||
|
public CommonResult<Void> resetPassword(@RequestParam("yhUuid") String yhUuid) {
|
||||||
|
authService.resetPassword(yhUuid);
|
||||||
|
return success(null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,29 @@
|
|||||||
|
package com.css.txw.sso.pojo.vo;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotBlank;
|
||||||
|
|
||||||
|
@Schema(description = "修改密码 Request VO")
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Builder
|
||||||
|
public class ChangePasswordReqVO {
|
||||||
|
|
||||||
|
@Schema(description = "旧密码(MD5加密)", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
|
@NotBlank(message = "旧密码不能为空")
|
||||||
|
private String oldPassword;
|
||||||
|
|
||||||
|
@Schema(description = "新密码(MD5加密)", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
|
@NotBlank(message = "新密码不能为空")
|
||||||
|
private String newPassword;
|
||||||
|
|
||||||
|
@Schema(description = "确认密码(MD5加密)", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
|
@NotBlank(message = "确认密码不能为空")
|
||||||
|
private String confirmPassword;
|
||||||
|
}
|
||||||
@ -3,6 +3,7 @@ package com.css.txw.sso.service.auth;
|
|||||||
import com.css.txw.mhzc.pojo.YhxxbDTO;
|
import com.css.txw.mhzc.pojo.YhxxbDTO;
|
||||||
import com.css.txw.sso.pojo.vo.AuthLoginReqVO;
|
import com.css.txw.sso.pojo.vo.AuthLoginReqVO;
|
||||||
import com.css.txw.sso.pojo.vo.AuthLoginRespVO;
|
import com.css.txw.sso.pojo.vo.AuthLoginRespVO;
|
||||||
|
import com.css.txw.sso.pojo.vo.ChangePasswordReqVO;
|
||||||
import com.css.txw.sso.pojo.vo.DidBindPhoneReqVO;
|
import com.css.txw.sso.pojo.vo.DidBindPhoneReqVO;
|
||||||
import com.css.txw.sso.pojo.vo.SMSLoginReqVO;
|
import com.css.txw.sso.pojo.vo.SMSLoginReqVO;
|
||||||
import com.css.txw.sso.pojo.vo.SendMsgReqVO;
|
import com.css.txw.sso.pojo.vo.SendMsgReqVO;
|
||||||
@ -52,4 +53,19 @@ public interface AuthService {
|
|||||||
AuthLoginRespVO loginBySMS(SMSLoginReqVO reqVO);
|
AuthLoginRespVO loginBySMS(SMSLoginReqVO reqVO);
|
||||||
|
|
||||||
AuthLoginRespVO didBindPhone(@Valid DidBindPhoneReqVO reqVO);
|
AuthLoginRespVO didBindPhone(@Valid DidBindPhoneReqVO reqVO);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改密码
|
||||||
|
*
|
||||||
|
* @param token 当前用户的访问令牌
|
||||||
|
* @param reqVO 修改密码请求
|
||||||
|
*/
|
||||||
|
void changePassword(String token, ChangePasswordReqVO reqVO);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重置密码(管理员操作,重置为默认密码)
|
||||||
|
*
|
||||||
|
* @param yhUuid 用户UUID
|
||||||
|
*/
|
||||||
|
void resetPassword(String yhUuid);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,6 +6,9 @@ import static com.css.ggzc.framework.common.exception.util.ServiceExceptionUtil.
|
|||||||
import static com.css.txw.sso.constants.ErrorCodeConstants.AUTH_LOGIN_BAD_CREDENTIALS;
|
import static com.css.txw.sso.constants.ErrorCodeConstants.AUTH_LOGIN_BAD_CREDENTIALS;
|
||||||
import static com.css.txw.sso.constants.ErrorCodeConstants.AUTH_LOGIN_CAPTCHA_CODE_ERROR;
|
import static com.css.txw.sso.constants.ErrorCodeConstants.AUTH_LOGIN_CAPTCHA_CODE_ERROR;
|
||||||
import static com.css.txw.sso.constants.ErrorCodeConstants.AUTH_LOGIN_PASSWORD_ERROR_LOCK;
|
import static com.css.txw.sso.constants.ErrorCodeConstants.AUTH_LOGIN_PASSWORD_ERROR_LOCK;
|
||||||
|
import static com.css.txw.sso.constants.ErrorCodeConstants.AUTH_PASSWORD_CONFIRM_MISMATCH;
|
||||||
|
import static com.css.txw.sso.constants.ErrorCodeConstants.AUTH_PASSWORD_COMPLEXITY_INVALID;
|
||||||
|
import static com.css.txw.sso.constants.ErrorCodeConstants.AUTH_PASSWORD_SAME_AS_OLD;
|
||||||
import static com.css.txw.sso.constants.ErrorCodeConstants.OAUTH2_LOGIN_SJHM_NOT_EXISTS;
|
import static com.css.txw.sso.constants.ErrorCodeConstants.OAUTH2_LOGIN_SJHM_NOT_EXISTS;
|
||||||
import static com.css.txw.sso.constants.ErrorCodeConstants.OAUTH2_LOGIN_SMS_NOT_EXISTS;
|
import static com.css.txw.sso.constants.ErrorCodeConstants.OAUTH2_LOGIN_SMS_NOT_EXISTS;
|
||||||
import static com.css.txw.sso.constants.ErrorCodeConstants.OAUTH2_SJHM_LOCK;
|
import static com.css.txw.sso.constants.ErrorCodeConstants.OAUTH2_SJHM_LOCK;
|
||||||
@ -37,6 +40,7 @@ import com.css.ggzc.framework.common.util.json.JsonUtils;
|
|||||||
import com.css.txw.common.pojo.dto.sms.SMSResDTO;
|
import com.css.txw.common.pojo.dto.sms.SMSResDTO;
|
||||||
import com.css.txw.common.service.ISMService;
|
import com.css.txw.common.service.ISMService;
|
||||||
import com.css.txw.mhzc.pojo.YhxxbDTO;
|
import com.css.txw.mhzc.pojo.YhxxbDTO;
|
||||||
|
import com.css.txw.sso.pojo.dto.session.SessionInfo;
|
||||||
import com.css.txw.sso.configuration.SMSClient;
|
import com.css.txw.sso.configuration.SMSClient;
|
||||||
import com.css.txw.sso.controller.thirdparty.DidController;
|
import com.css.txw.sso.controller.thirdparty.DidController;
|
||||||
import com.css.txw.sso.convert.AuthConvert;
|
import com.css.txw.sso.convert.AuthConvert;
|
||||||
@ -44,6 +48,7 @@ import com.css.txw.sso.pojo.domain.oauth2.OAuth2AccessTokenDO;
|
|||||||
import com.css.txw.sso.pojo.domain.oauth2.OAuth2ClientDO;
|
import com.css.txw.sso.pojo.domain.oauth2.OAuth2ClientDO;
|
||||||
import com.css.txw.sso.pojo.vo.AuthLoginReqVO;
|
import com.css.txw.sso.pojo.vo.AuthLoginReqVO;
|
||||||
import com.css.txw.sso.pojo.vo.AuthLoginRespVO;
|
import com.css.txw.sso.pojo.vo.AuthLoginRespVO;
|
||||||
|
import com.css.txw.sso.pojo.vo.ChangePasswordReqVO;
|
||||||
import com.css.txw.sso.pojo.vo.DidBindPhoneReqVO;
|
import com.css.txw.sso.pojo.vo.DidBindPhoneReqVO;
|
||||||
import com.css.txw.sso.pojo.vo.SMSLoginReqVO;
|
import com.css.txw.sso.pojo.vo.SMSLoginReqVO;
|
||||||
import com.css.txw.sso.pojo.vo.SendMsgReqVO;
|
import com.css.txw.sso.pojo.vo.SendMsgReqVO;
|
||||||
@ -324,4 +329,69 @@ public class AuthServiceImpl implements AuthService {
|
|||||||
return String.format(PHONE_LOCK, phone);
|
return String.format(PHONE_LOCK, phone);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void changePassword(String token, ChangePasswordReqVO reqVO) {
|
||||||
|
// 1. 校验新密码与确认密码一致性
|
||||||
|
if (!reqVO.getNewPassword().equals(reqVO.getConfirmPassword())) {
|
||||||
|
throw exception(AUTH_PASSWORD_CONFIRM_MISMATCH);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 密码复杂度校验(6-20位,字母+数字)
|
||||||
|
if (!isValidPasswordComplexity(reqVO.getNewPassword())) {
|
||||||
|
throw exception(AUTH_PASSWORD_COMPLEXITY_INVALID);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 通过token解析获取当前登录用户
|
||||||
|
log.info("changePassword token: {}", token);
|
||||||
|
SessionInfo sessionInfo = oauth2TokenService.checkAccessToken(token);
|
||||||
|
log.info("changePassword sessionInfo: {}", sessionInfo);
|
||||||
|
if (sessionInfo == null) {
|
||||||
|
log.error("changePassword token解析失败, token: {}", token);
|
||||||
|
throw exception(AUTH_LOGIN_BAD_CREDENTIALS);
|
||||||
|
}
|
||||||
|
// 优先使用currentYhuuid,如果为空则使用tokenInfo中的yhUuid
|
||||||
|
String yhUuid = sessionInfo.getCurrentYhuuid();
|
||||||
|
if (GyUtils.isNull(yhUuid)) {
|
||||||
|
yhUuid = sessionInfo.getTokenInfo().getYhUuid();
|
||||||
|
}
|
||||||
|
if (GyUtils.isNull(yhUuid)) {
|
||||||
|
yhUuid = sessionInfo.getYhxx().getYhuuid();
|
||||||
|
}
|
||||||
|
log.info("changePassword yhUuid from token: {}", yhUuid);
|
||||||
|
YhxxbDTO yhxx = yhxxService.getYhxx(yhUuid);
|
||||||
|
log.info("changePassword yhxx: {}", yhxx);
|
||||||
|
if (yhxx == null) {
|
||||||
|
log.error("changePassword 用户不存在, yhUuid: {}", yhUuid);
|
||||||
|
throw exception(AUTH_LOGIN_BAD_CREDENTIALS);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 校验新旧密码不能相同
|
||||||
|
if (reqVO.getOldPassword().equals(reqVO.getNewPassword())) {
|
||||||
|
throw exception(AUTH_PASSWORD_SAME_AS_OLD);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. 旧密码正确性校验
|
||||||
|
authenticate(yhxx.getDlzh(), reqVO.getOldPassword());
|
||||||
|
|
||||||
|
// 6. 更新密码(SSO已验证旧密码,mhzc再次验证后更新)
|
||||||
|
yhxxService.updatePassword(yhxx.getYhUuid(), reqVO.getNewPassword(), reqVO.getOldPassword());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void resetPassword(String yhUuid) {
|
||||||
|
// 获取默认密码(从配置中读取)
|
||||||
|
String defaultPassword = CacheUtils.dm2mc("cs_ggzc_xtcs", "dlmm");
|
||||||
|
// 调用 mhzc 更新密码
|
||||||
|
yhxxService.resetPassword(yhUuid, defaultPassword);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isValidPasswordComplexity(String password) {
|
||||||
|
if (password == null || password.length() < 6 || password.length() > 20) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
boolean hasLetter = password.matches(".*[A-Za-z]+.*");
|
||||||
|
boolean hasDigit = password.matches(".*[0-9]+.*");
|
||||||
|
return hasLetter && hasDigit;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -41,4 +41,8 @@ public interface YhxxService {
|
|||||||
|
|
||||||
YhxxbDTO updateDid(YhxxbDTO yhxx);
|
YhxxbDTO updateDid(YhxxbDTO yhxx);
|
||||||
|
|
||||||
|
void updatePassword(String yhUuid, String newPassword, String oldPassword);
|
||||||
|
|
||||||
|
void resetPassword(String yhUuid, String newPassword);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -243,5 +243,22 @@ public class YhxxServiceImpl implements YhxxService {
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updatePassword(String yhUuid, String newPassword, String oldPassword) {
|
||||||
|
YhxxReqDTO reqDTO = new YhxxReqDTO();
|
||||||
|
reqDTO.setYhuuid(yhUuid);
|
||||||
|
reqDTO.setDlmm(newPassword);
|
||||||
|
reqDTO.setOldPassword(oldPassword);
|
||||||
|
mhzcApi.updatePassword(reqDTO);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void resetPassword(String yhUuid, String newPassword) {
|
||||||
|
YhxxReqDTO reqDTO = new YhxxReqDTO();
|
||||||
|
reqDTO.setYhuuid(yhUuid);
|
||||||
|
reqDTO.setDlmm(newPassword);
|
||||||
|
mhzcApi.resetPassword(reqDTO);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -67,8 +67,8 @@ export const getAllUser = (params) => {
|
|||||||
// 重置账号密码
|
// 重置账号密码
|
||||||
export const resetPassword = (params) => {
|
export const resetPassword = (params) => {
|
||||||
return fetchSso({
|
return fetchSso({
|
||||||
url: `${basurl}/mhzc/user/resetPassword?yhUuid=${params}`,
|
url: `${basurl}/sso/auth/resetPassword?yhUuid=${params}`,
|
||||||
method: 'get',
|
method: 'post',
|
||||||
loading: true,
|
loading: true,
|
||||||
headers,
|
headers,
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user