添加 CaptchaSession人机验证服务

This commit is contained in:
tone
2024-09-25 23:21:48 +08:00
parent c2d98cf2c0
commit b3e6462280

View File

@@ -0,0 +1,125 @@
/**
* @file CaptchaSession.ts
* @version 1.0.0
* @description 旋转图像验证服务
*/
import Logger from '@lib/Logger/Logger';
import RedisConnection from '@lib/Database/RedisConnection';
import config from 'src/config';
type CaptchaSessionDataJSON = {
rotateDeg: number,
tryCount: number,
isPassed: boolean
}
class _CaptchaSession {
private readonly logger = new Logger('Service', 'captchaSession');
private readonly AllowMaxTryCount: number = config.service.captchaSession.allowMaxTryCount;
private readonly AllowMaxAngleDiff: number = config.service.captchaSession.allowMaxAngleDiff;
private readonly ExpriedTimeSec: number = config.service.captchaSession.expriedTimeSec;
private readonly RedisCommonKey: string = 'Service:captchaSession:';
constructor() {
this.logger.info('Rotation image verification service has started');
}
private async get(session: string): Promise<CaptchaSessionDataJSON | undefined> {
try {
const result = await RedisConnection.get(this.RedisCommonKey + session);
if (result === null)
return;
return JSON.parse(result);
} catch (error) {
this.logger.error(`Error occurred while retrieving session [${session}]: ${error}`);
return;
}
}
private async remove(session: string): Promise<void> {
try {
await RedisConnection.del(this.RedisCommonKey + session);
} catch (error) {
this.logger.error(`Failed to delete session [${session}]`);
}
}
/**
*
* @param session 验证会话标识符
* @param rotateDeg 图片旋转角度
* @returns true存储成功 false存储失败
*/
public async add(session: string, rotateDeg: number): Promise<boolean> {
const result: CaptchaSessionDataJSON = {
rotateDeg: rotateDeg,
tryCount: 0,
isPassed: false
}
try {
const res = await RedisConnection.set(this.RedisCommonKey + session, JSON.stringify(result));
if (res && res === 'OK') {
RedisConnection.expire(this.RedisCommonKey + session, this.ExpriedTimeSec);
this.logger.info(`Session [${session}] and rotation angle [${rotateDeg}] have been stored`);
return true;
}
this.logger.error(`Failed to store session [${session}] and rotation angle [${rotateDeg}]`);
return false;
} catch (error) {
this.logger.error(`Failed to store session [${session}] and rotation angle [${rotateDeg}]: ${error}`);
return false;
}
}
/**
*
* @param session 验证会话标识符
* @returns true已验证过期 false未通过
*/
public async isPassed(session: string): Promise<boolean> {
const result = await this.get(session);
if (!result)
return false;
if (result.isPassed)
this.remove(session);
return result.isPassed;
}
/**
*
* @param session 验证会话标识符
* @param rotateDeg 图片旋转角度
* @returns 0验证已过期或服务器错误 1通过验证 -1超过最大允许尝试次数 -2角度差异过大
*/
public async check(session: string, rotateDeg: number): Promise<number> {
try {
let result = await this.get(session);
if (!result) {
return 0;
}
if (result.isPassed) {
this.logger.info(`Session [${session}] has already been verified, no need to verify again`);
return 1;
}
if (Math.abs(result.rotateDeg - rotateDeg) <= this.AllowMaxAngleDiff) {
result.isPassed = true;
await RedisConnection.del(this.RedisCommonKey + session);
await RedisConnection.set(this.RedisCommonKey + session, JSON.stringify(result));
RedisConnection.expire(this.RedisCommonKey + session, this.ExpriedTimeSec);
return 1;
}
result.tryCount++;
if (result.tryCount >= this.AllowMaxTryCount) {
this.remove(session);
return -1;
}
RedisConnection.set(this.RedisCommonKey + session, JSON.stringify(result));
return -2;
} catch (error) {
this.logger.error(`Error occurred while checking session [${session}]: ${error}`);
return 0;
}
}
}
const CaptchaSession = new _CaptchaSession();
export default CaptchaSession;