fix:修复验证码验证时TTL被重置的问题
- 修复验证失败时TTL被重置为5分钟的bug - 保持原有的过期时间,不重置验证码有效期 - 增加详细的TTL变化日志记录 - 改进错误处理和边界情况处理 - 解决用户验证一次错误后验证码立即过期的问题
This commit is contained in:
@@ -111,24 +111,50 @@ export class VerificationService {
|
||||
const codeInfoStr = await this.redis.get(key);
|
||||
|
||||
if (!codeInfoStr) {
|
||||
this.logger.warn(`验证码不存在或已过期: ${identifier} (${type})`);
|
||||
throw new BadRequestException('验证码不存在或已过期');
|
||||
}
|
||||
|
||||
const codeInfo: VerificationCodeInfo = JSON.parse(codeInfoStr);
|
||||
let codeInfo: VerificationCodeInfo;
|
||||
try {
|
||||
codeInfo = JSON.parse(codeInfoStr);
|
||||
} catch (error) {
|
||||
this.logger.error(`验证码数据解析失败: ${identifier} (${type})`, error);
|
||||
await this.redis.del(key);
|
||||
throw new BadRequestException('验证码数据异常,请重新获取');
|
||||
}
|
||||
|
||||
// 检查尝试次数
|
||||
if (codeInfo.attempts >= codeInfo.maxAttempts) {
|
||||
this.logger.warn(`验证码尝试次数已达上限: ${identifier} (${type}), 尝试次数: ${codeInfo.attempts}`);
|
||||
await this.redis.del(key);
|
||||
throw new BadRequestException('验证码尝试次数过多,请重新获取');
|
||||
}
|
||||
|
||||
// 增加尝试次数
|
||||
codeInfo.attempts++;
|
||||
await this.redis.set(key, JSON.stringify(codeInfo), this.CODE_EXPIRE_TIME);
|
||||
// 获取当前TTL
|
||||
const currentTTL = await this.redis.ttl(key);
|
||||
this.logger.debug(`验证码当前TTL: ${identifier} (${type}), TTL: ${currentTTL}秒`);
|
||||
|
||||
// 验证验证码
|
||||
if (codeInfo.code !== inputCode) {
|
||||
this.logger.warn(`验证码验证失败: ${identifier} (${type}) - 剩余尝试次数: ${codeInfo.maxAttempts - codeInfo.attempts}`);
|
||||
// 增加尝试次数
|
||||
codeInfo.attempts++;
|
||||
|
||||
// 保持原有的TTL,不重置过期时间
|
||||
if (currentTTL > 0) {
|
||||
// 使用剩余的TTL时间
|
||||
await this.redis.set(key, JSON.stringify(codeInfo), currentTTL);
|
||||
this.logger.warn(`验证码验证失败: ${identifier} (${type}) - 剩余尝试次数: ${codeInfo.maxAttempts - codeInfo.attempts}, 剩余时间: ${currentTTL}秒`);
|
||||
} else if (currentTTL === -1) {
|
||||
// 永不过期的情况,保持永不过期
|
||||
await this.redis.set(key, JSON.stringify(codeInfo));
|
||||
this.logger.warn(`验证码验证失败: ${identifier} (${type}) - 剩余尝试次数: ${codeInfo.maxAttempts - codeInfo.attempts}, 永不过期`);
|
||||
} else {
|
||||
// TTL为-2表示键不存在,这种情况理论上不应该发生
|
||||
this.logger.error(`验证码TTL异常: ${identifier} (${type}), TTL: ${currentTTL}`);
|
||||
throw new BadRequestException('验证码状态异常,请重新获取');
|
||||
}
|
||||
|
||||
throw new BadRequestException(`验证码错误,剩余尝试次数: ${codeInfo.maxAttempts - codeInfo.attempts}`);
|
||||
}
|
||||
|
||||
@@ -288,13 +314,16 @@ export class VerificationService {
|
||||
ttl: number;
|
||||
attempts?: number;
|
||||
maxAttempts?: number;
|
||||
code?: string;
|
||||
createdAt?: number;
|
||||
}> {
|
||||
const key = this.buildRedisKey(identifier, type);
|
||||
const exists = await this.redis.exists(key);
|
||||
const ttl = await this.redis.ttl(key);
|
||||
|
||||
if (!exists) {
|
||||
return { exists: false, ttl: -1 };
|
||||
this.logger.debug(`验证码不存在: ${identifier} (${type})`);
|
||||
return { exists: false, ttl: -2 };
|
||||
}
|
||||
|
||||
const codeInfoStr = await this.redis.get(key);
|
||||
@@ -307,11 +336,54 @@ export class VerificationService {
|
||||
codeInfo = {} as VerificationCodeInfo;
|
||||
}
|
||||
|
||||
this.logger.debug(`验证码统计: ${identifier} (${type}), TTL: ${ttl}, 尝试次数: ${codeInfo.attempts}/${codeInfo.maxAttempts}`);
|
||||
|
||||
return {
|
||||
exists: true,
|
||||
ttl,
|
||||
attempts: codeInfo.attempts,
|
||||
maxAttempts: codeInfo.maxAttempts,
|
||||
code: codeInfo.code, // 仅用于调试,生产环境应该移除
|
||||
createdAt: codeInfo.createdAt,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 调试方法:获取验证码详细信息
|
||||
* 仅用于开发和调试,生产环境应该移除或限制访问
|
||||
*
|
||||
* @param identifier 标识符
|
||||
* @param type 验证码类型
|
||||
* @returns 详细信息
|
||||
*/
|
||||
async debugCodeInfo(identifier: string, type: VerificationCodeType): Promise<any> {
|
||||
const key = this.buildRedisKey(identifier, type);
|
||||
|
||||
const [exists, ttl, rawData] = await Promise.all([
|
||||
this.redis.exists(key),
|
||||
this.redis.ttl(key),
|
||||
this.redis.get(key)
|
||||
]);
|
||||
|
||||
const result = {
|
||||
key,
|
||||
exists,
|
||||
ttl,
|
||||
rawData,
|
||||
parsedData: null as any,
|
||||
currentTime: Date.now(),
|
||||
timeFormatted: new Date().toISOString()
|
||||
};
|
||||
|
||||
if (rawData) {
|
||||
try {
|
||||
result.parsedData = JSON.parse(rawData);
|
||||
} catch (error) {
|
||||
result.parsedData = { error: 'JSON解析失败', raw: rawData };
|
||||
}
|
||||
}
|
||||
|
||||
this.logger.debug(`调试验证码信息: ${JSON.stringify(result, null, 2)}`);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user