添加 CaptchaSession人机验证服务
This commit is contained in:
125
src/lib/Service/CaptchaSession.ts
Normal file
125
src/lib/Service/CaptchaSession.ts
Normal 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;
|
||||||
Reference in New Issue
Block a user