update:完成密码找回功能

This commit is contained in:
hrz
2025-05-19 21:19:24 +08:00
parent f0e55037de
commit 848f73715f
7 changed files with 64 additions and 57 deletions
@@ -84,6 +84,7 @@ public class ShiroConfig {
filterMap.put("/user/login", "anon"); filterMap.put("/user/login", "anon");
filterMap.put("/user/pub-config", "anon"); filterMap.put("/user/pub-config", "anon");
filterMap.put("/user/register", "anon"); filterMap.put("/user/register", "anon");
filterMap.put("/user/retrieve-password", "anon");
// 将config路径使用server服务过滤器 // 将config路径使用server服务过滤器
filterMap.put("/config/**", "server"); filterMap.put("/config/**", "server");
filterMap.put("/agent/chat-history/report", "server"); filterMap.put("/agent/chat-history/report", "server");
@@ -174,19 +174,19 @@ public class LoginController {
// 验证用户是否是手机号码 // 验证用户是否是手机号码
boolean validPhone = ValidatorUtils.isValidPhone(dto.getPhone()); boolean validPhone = ValidatorUtils.isValidPhone(dto.getPhone());
if (!validPhone) { if (!validPhone) {
throw new RenException("用户名不是手机号码,无法提供找回密码服务"); throw new RenException("输入的手机号码格式不正确");
} }
// 按照用户名获取用户 // 按照用户名获取用户
SysUserDTO userDTO = sysUserService.getByUsername(dto.getPhone()); SysUserDTO userDTO = sysUserService.getByUsername(dto.getPhone());
if (userDTO == null) { if (userDTO == null) {
throw new RenException("用户名或验证码错误"); throw new RenException("输入的手机号码未注册");
} }
// 验证短信验证码是否正常 // 验证短信验证码是否正常
boolean validate = captchaService.validateSMSValidateCode(dto.getPhone(), dto.getCode(), false); boolean validate = captchaService.validateSMSValidateCode(dto.getPhone(), dto.getCode(), false);
// 判断是否通过验证 // 判断是否通过验证
if (!validate) { if (!validate) {
throw new RenException("用户名或验证码错误"); throw new RenException("输入的手机验证码错误");
} }
sysUserService.changePasswordDirectly(userDTO.getId(), dto.getPassword()); sysUserService.changePasswordDirectly(userDTO.getId(), dto.getPassword());
@@ -119,7 +119,7 @@ public class CaptchaServiceImpl implements CaptchaService {
// 更新今日发送次数 // 更新今日发送次数
if (todayCount == 0) { if (todayCount == 0) {
redisUtils.increment(todayCountKey,RedisUtils.DEFAULT_EXPIRE); redisUtils.increment(todayCountKey, RedisUtils.DEFAULT_EXPIRE);
} else { } else {
redisUtils.increment(todayCountKey); redisUtils.increment(todayCountKey);
} }
@@ -133,6 +133,10 @@ public class SysUserServiceImpl extends BaseServiceImpl<SysUserDao, SysUserEntit
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public void changePasswordDirectly(Long userId, String password) { public void changePasswordDirectly(Long userId, String password) {
// 新密码强度
if (!isStrongPassword(password)) {
throw new RenException(ErrorCode.PASSWORD_WEAK_ERROR);
}
SysUserEntity sysUserEntity = new SysUserEntity(); SysUserEntity sysUserEntity = new SysUserEntity();
sysUserEntity.setId(userId); sysUserEntity.setId(userId);
sysUserEntity.setPassword(PasswordUtils.encode(password)); sysUserEntity.setPassword(PasswordUtils.encode(password));
+2 -2
View File
@@ -56,13 +56,13 @@
<div <div
style="font-weight: 400;font-size: 14px;text-align: left;color: #5778ff;display: flex;justify-content: space-between;margin-top: 20px;"> style="font-weight: 400;font-size: 14px;text-align: left;color: #5778ff;display: flex;justify-content: space-between;margin-top: 20px;">
<div v-if="allowUserRegister" style="cursor: pointer;" @click="goToRegister">新用户注册</div> <div v-if="allowUserRegister" style="cursor: pointer;" @click="goToRegister">新用户注册</div>
<div style="cursor: pointer;" @click="goToForgetPassword">忘记密码?</div> <div style="cursor: pointer;" @click="goToForgetPassword" v-if="enableMobileRegister">忘记密码?</div>
</div> </div>
</div> </div>
<div class="login-btn" @click="login">登录</div> <div class="login-btn" @click="login">登录</div>
<!-- 登录方式切换按钮 --> <!-- 登录方式切换按钮 -->
<div class="login-type-container"> <div class="login-type-container" v-if="enableMobileRegister">
<el-tooltip content="手机号码登录" placement="bottom"> <el-tooltip content="手机号码登录" placement="bottom">
<el-button :type="isMobileLogin ? 'primary' : 'default'" icon="el-icon-mobile" circle <el-button :type="isMobileLogin ? 'primary' : 'default'" icon="el-icon-mobile" circle
@click="switchLoginType('mobile')"></el-button> @click="switchLoginType('mobile')"></el-button>
+1
View File
@@ -201,6 +201,7 @@ export default {
// 验证图形验证码 // 验证图形验证码
if (!this.validateInput(this.form.captcha, '请输入图形验证码')) { if (!this.validateInput(this.form.captcha, '请输入图形验证码')) {
this.fetchCaptcha();
return; return;
} }
+52 -51
View File
@@ -24,56 +24,56 @@
</div> </div>
<div style="padding: 0 30px;"> <div style="padding: 0 30px;">
<!-- 手机号输入 --> <!-- 手机号输入 -->
<div class="input-box"> <div class="input-box">
<div style="display: flex; align-items: center; width: 100%;"> <div style="display: flex; align-items: center; width: 100%;">
<el-select v-model="form.areaCode" style="width: 220px; margin-right: 10px;"> <el-select v-model="form.areaCode" style="width: 220px; margin-right: 10px;">
<el-option v-for="item in mobileAreaList" :key="item.key" <el-option v-for="item in mobileAreaList" :key="item.key" :label="`${item.name} (${item.key})`"
:label="`${item.name} (${item.key})`" :value="item.key" /> :value="item.key" />
</el-select> </el-select>
<el-input v-model="form.mobile" placeholder="请输入手机号码" /> <el-input v-model="form.mobile" placeholder="请输入手机号码" />
</div>
</div>
<div style="display: flex; align-items: center; margin-top: 20px; width: 100%; gap: 10px;">
<div class="input-box" style="width: calc(100% - 130px); margin-top: 0;">
<img loading="lazy" alt="" class="input-icon" src="@/assets/login/shield.png" />
<el-input v-model="form.captcha" placeholder="请输入验证码" style="flex: 1;" />
</div>
<img loading="lazy" v-if="captchaUrl" :src="captchaUrl" alt="验证码"
style="width: 150px; height: 40px; cursor: pointer;" @click="fetchCaptcha" />
</div>
<!-- 手机验证码 -->
<div style="display: flex; align-items: center; margin-top: 20px; width: 100%; gap: 10px;">
<div class="input-box" style="width: calc(100% - 130px); margin-top: 0;">
<img loading="lazy" alt="" class="input-icon" src="@/assets/login/phone.png" />
<el-input v-model="form.mobileCaptcha" placeholder="请输入手机验证码" style="flex: 1;" maxlength="6" />
</div>
<el-button type="primary" class="send-captcha-btn" :disabled="!canSendMobileCaptcha"
@click="sendMobileCaptcha">
<span>
{{ countdown > 0 ? `${countdown}秒后重试` : '发送验证码' }}
</span>
</el-button>
</div>
<!-- 新密码 -->
<div class="input-box">
<img loading="lazy" alt="" class="input-icon" src="@/assets/login/password.png" />
<el-input v-model="form.newPassword" placeholder="请输入新密码" type="password" />
</div> </div>
</div>
<!-- 确认新密码 --> <div style="display: flex; align-items: center; margin-top: 20px; width: 100%; gap: 10px;">
<div class="input-box"> <div class="input-box" style="width: calc(100% - 130px); margin-top: 0;">
<img loading="lazy" alt="" class="input-icon" src="@/assets/login/password.png" /> <img loading="lazy" alt="" class="input-icon" src="@/assets/login/shield.png" />
<el-input v-model="form.confirmPassword" placeholder="请确认新密码" type="password" /> <el-input v-model="form.captcha" placeholder="请输入验证码" style="flex: 1;" />
</div> </div>
<img loading="lazy" v-if="captchaUrl" :src="captchaUrl" alt="验证码"
style="width: 150px; height: 40px; cursor: pointer;" @click="fetchCaptcha" />
</div>
<!-- 修改底部链接 --> <!-- 手机验证码 -->
<div style="font-weight: 400;font-size: 14px;text-align: left;color: #5778ff;margin-top: 20px;"> <div style="display: flex; align-items: center; margin-top: 20px; width: 100%; gap: 10px;">
<div style="cursor: pointer;" @click="goToLogin">返回登录</div> <div class="input-box" style="width: calc(100% - 130px); margin-top: 0;">
<img loading="lazy" alt="" class="input-icon" src="@/assets/login/phone.png" />
<el-input v-model="form.mobileCaptcha" placeholder="请输入手机验证码" style="flex: 1;" maxlength="6" />
</div> </div>
<el-button type="primary" class="send-captcha-btn" :disabled="!canSendMobileCaptcha"
@click="sendMobileCaptcha">
<span>
{{ countdown > 0 ? `${countdown}秒后重试` : '发送验证码' }}
</span>
</el-button>
</div>
<!-- 新密码 -->
<div class="input-box">
<img loading="lazy" alt="" class="input-icon" src="@/assets/login/password.png" />
<el-input v-model="form.newPassword" placeholder="请输入新密码" type="password" />
</div>
<!-- 确认新密码 -->
<div class="input-box">
<img loading="lazy" alt="" class="input-icon" src="@/assets/login/password.png" />
<el-input v-model="form.confirmPassword" placeholder="请确认新密码" type="password" />
</div>
<!-- 修改底部链接 -->
<div style="font-weight: 400;font-size: 14px;text-align: left;color: #5778ff;margin-top: 20px;">
<div style="cursor: pointer;" @click="goToLogin">返回登录</div>
</div>
</div> </div>
<!-- 修改按钮文本 --> <!-- 修改按钮文本 -->
@@ -171,6 +171,7 @@ export default {
// 验证图形验证码 // 验证图形验证码
if (!this.validateInput(this.form.captcha, '请输入图形验证码')) { if (!this.validateInput(this.form.captcha, '请输入图形验证码')) {
this.fetchCaptcha();
return; return;
} }
@@ -225,18 +226,18 @@ export default {
return; return;
} }
Api.user.resetPassword({ Api.user.retrievePassword({
phone: this.form.areaCode + this.form.mobile, phone: this.form.areaCode + this.form.mobile,
newPassword: this.form.newPassword, password: this.form.newPassword,
smsCode: this.form.mobileCaptcha, code: this.form.mobileCaptcha
captcha: this.form.captcha,
captchaId: this.form.captchaId
}, (res) => { }, (res) => {
showSuccess('密码重置成功'); showSuccess('密码重置成功');
goToPage('/login'); goToPage('/login');
}, (err) => { }, (err) => {
showDanger(err.data.msg || '重置失败'); showDanger(err.data.msg || '重置失败');
this.fetchCaptcha(); if (err.data != null && err.data.msg != null && err.data.msg.indexOf('图形验证码') > -1) {
this.fetchCaptcha()
}
}); });
}, },