diff --git a/packages/ui/certd-server/src/controller/basic/code-controller.ts b/packages/ui/certd-server/src/controller/basic/code-controller.ts
index fb0eedc2..bb7c0f6e 100644
--- a/packages/ui/certd-server/src/controller/basic/code-controller.ts
+++ b/packages/ui/certd-server/src/controller/basic/code-controller.ts
@@ -16,6 +16,9 @@ export class SmsCodeReq {
@Rule(RuleType.string().required().max(4))
imgCode: string;
+
+ @Rule(RuleType.string())
+ verificationType: string;
}
export class EmailCodeReq {
@@ -32,6 +35,9 @@ export class EmailCodeReq {
verificationType: string;
}
+// 找回密码的验证码有效期
+const FORGOT_PASSWORD_CODE_DURATION = 3
+
/**
*/
@Provide()
@@ -48,8 +54,18 @@ export class BasicController extends BaseController {
@Body(ALL)
body: SmsCodeReq
) {
+ const opts = {
+ verificationType: body.verificationType,
+ verificationCodeLength: undefined,
+ duration: undefined,
+ };
+ if(body?.verificationType === 'forgotPassword') {
+ opts.duration = FORGOT_PASSWORD_CODE_DURATION;
+ // opts.verificationCodeLength = 6; //部分厂商这里会设置参数长度这里就不改了
+ }
+
await this.codeService.checkCaptcha(body.randomStr, body.imgCode);
- await this.codeService.sendSmsCode(body.phoneCode, body.mobile, body.randomStr);
+ await this.codeService.sendSmsCode(body.phoneCode, body.mobile, body.randomStr, opts);
return this.ok(null);
}
@@ -60,6 +76,7 @@ export class BasicController extends BaseController {
) {
const opts = {
verificationType: body.verificationType,
+ verificationCodeLength: undefined,
title: undefined,
content: undefined,
duration: undefined,
@@ -67,7 +84,8 @@ export class BasicController extends BaseController {
if(body?.verificationType === 'forgotPassword') {
opts.title = '找回密码';
opts.content = '验证码:${code}。您正在找回密码,请输入验证码并完成操作。如非本人操作请忽略';
- opts.duration = 3;
+ opts.duration = FORGOT_PASSWORD_CODE_DURATION;
+ opts.verificationCodeLength = 6;
}
await this.codeService.checkCaptcha(body.randomStr, body.imgCode);
diff --git a/packages/ui/certd-server/src/controller/user/login/forgot-password-controller.ts b/packages/ui/certd-server/src/controller/user/login/forgot-password-controller.ts
index a497504b..4c60d394 100644
--- a/packages/ui/certd-server/src/controller/user/login/forgot-password-controller.ts
+++ b/packages/ui/certd-server/src/controller/user/login/forgot-password-controller.ts
@@ -28,6 +28,8 @@ export class LoginController extends BaseController {
if(!sysSettings.selfServicePasswordRetrievalEnabled) {
throw new CommonException('暂未开启自助找回');
}
+ // 找回密码的验证码允许错误次数
+ const errorNum = 5;
if(body.type === 'email') {
this.codeService.checkEmailCode({
@@ -35,6 +37,7 @@ export class LoginController extends BaseController {
email: body.input,
randomStr: body.randomStr,
validateCode: body.validateCode,
+ errorNum,
throwError: true,
});
} else if(body.type === 'mobile') {
@@ -44,6 +47,7 @@ export class LoginController extends BaseController {
randomStr: body.randomStr,
phoneCode: body.phoneCode,
smsCode: body.validateCode,
+ errorNum,
throwError: true,
});
} else {
diff --git a/packages/ui/certd-server/src/modules/basic/service/code-service.ts b/packages/ui/certd-server/src/modules/basic/service/code-service.ts
index 62d108ca..c84ab3b1 100644
--- a/packages/ui/certd-server/src/modules/basic/service/code-service.ts
+++ b/packages/ui/certd-server/src/modules/basic/service/code-service.ts
@@ -63,7 +63,8 @@ export class CodeService {
randomStr: string,
opts?: {
duration?: number,
- verificationType?: string
+ verificationType?: string,
+ verificationCodeLength?: number,
},
) {
if (!mobile) {
@@ -73,7 +74,8 @@ export class CodeService {
throw new Error('randomStr不能为空');
}
- const duration = Math.max(Math.floor(Math.min(opts?.duration || 5, 15)), 1);
+ const verificationCodeLength = Math.floor(Math.max(Math.min(opts?.verificationCodeLength || 4, 8), 4));
+ const duration = Math.floor(Math.max(Math.min(opts?.duration || 5, 15), 1));
const sysSettings = await this.sysSettingsService.getPrivateSettings();
if (!sysSettings.sms?.config?.accessId) {
@@ -87,7 +89,7 @@ export class CodeService {
accessService: accessGetter,
config: smsConfig,
});
- const smsCode = randomNumber(4);
+ const smsCode = randomNumber(verificationCodeLength);
await sender.sendSmsCode({
mobile,
code: smsCode,
@@ -114,7 +116,8 @@ export class CodeService {
title?: string,
content?: string,
duration?: number,
- verificationType?: string
+ verificationType?: string,
+ verificationCodeLength?: number,
},
) {
if (!email) {
@@ -132,8 +135,10 @@ export class CodeService {
}
}
- const code = randomNumber(4);
- const duration = Math.max(Math.floor(Math.min(opts?.duration || 5, 15)), 1);
+ const verificationCodeLength = Math.floor(Math.max(Math.min(opts?.verificationCodeLength || 4, 8), 4));
+ const duration = Math.floor(Math.max(Math.min(opts?.duration || 5, 15), 1));
+
+ const code = randomNumber(verificationCodeLength);
const title = `【${siteTitle}】${!!opts?.title ? opts.title : '验证码'}`;
const content = !!opts.content ? this.compile(opts.content)({code, duration}) : `您的验证码是${code},请勿泄露`;
@@ -154,12 +159,12 @@ export class CodeService {
/**
* checkSms
*/
- async checkSmsCode(opts: { mobile: string; phoneCode: string; smsCode: string; randomStr: string; verificationType?: string; throwError: boolean }) {
+ async checkSmsCode(opts: { mobile: string; phoneCode: string; smsCode: string; randomStr: string; verificationType?: string; throwError: boolean; errorNum?: number }) {
const key = this.buildSmsCodeKey(opts.phoneCode, opts.mobile, opts.randomStr, opts.verificationType);
if (isDev()) {
return true;
}
- return this.checkValidateCode(key, opts.smsCode, opts.throwError);
+ return this.checkValidateCode(key, opts.smsCode, opts.throwError, opts.errorNum);
}
buildSmsCodeKey(phoneCode: string, mobile: string, randomStr: string, verificationType?: string) {
@@ -169,22 +174,38 @@ export class CodeService {
buildEmailCodeKey(email: string, randomStr: string, verificationType?: string) {
return ['email', verificationType, email, randomStr].filter(item => !!item).join(':');
}
- checkValidateCode(key: string, userCode: string, throwError = true) {
+ checkValidateCode(key: string, userCode: string, throwError = true, errorNum = 0) {
+ // 记录异常次数key
+ const err_num_key = key + ':err_num';
//验证图片验证码
const code = cache.get(key);
if (code == null || code !== userCode) {
+ let maxRetryCount = false;
+ if (!!code && errorNum > 0) {
+ const err_num = cache.get(err_num_key) || 0
+ if(err_num >= errorNum - 1) {
+ maxRetryCount = true;
+ cache.delete(key);
+ cache.delete(err_num_key);
+ } else {
+ cache.set(err_num_key, err_num + 1, {
+ ttl: 30 * 60 * 1000
+ });
+ }
+ }
if (throwError) {
- throw new CodeErrorException('验证码错误');
+ throw new CodeErrorException(!maxRetryCount ? '验证码错误': '验证码错误请获取新的验证码');
}
return false;
}
cache.delete(key);
+ cache.delete(err_num_key);
return true;
}
- checkEmailCode(opts: { randomStr: string; validateCode: string; email: string; verificationType?: string; throwError: boolean }) {
+ checkEmailCode(opts: { randomStr: string; validateCode: string; email: string; verificationType?: string; throwError: boolean; errorNum?: number }) {
const key = this.buildEmailCodeKey(opts.email, opts.randomStr, opts.verificationType);
- return this.checkValidateCode(key, opts.validateCode, opts.throwError);
+ return this.checkValidateCode(key, opts.validateCode, opts.throwError, opts.errorNum);
}
compile(templateString: string) {