mirror of https://github.com/certd/certd
perf: 登录失败增加重试次数限制及冷却时间
parent
8002a56efc
commit
954b6df360
|
@ -0,0 +1,12 @@
|
||||||
|
import { Constants } from '../constants.js';
|
||||||
|
import { BaseException } from './base-exception.js';
|
||||||
|
/**
|
||||||
|
* 通用异常
|
||||||
|
*/
|
||||||
|
export class LoginErrorException extends BaseException {
|
||||||
|
leftCount: number;
|
||||||
|
constructor(message, leftCount: number) {
|
||||||
|
super('LoginErrorException', Constants.res.loginError.code, message ? message : Constants.res.loginError.message);
|
||||||
|
this.leftCount = leftCount;
|
||||||
|
}
|
||||||
|
}
|
|
@ -24,6 +24,20 @@ export class LoginController extends BaseController {
|
||||||
return this.ok(token);
|
return this.ok(token);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Post('/loginBySms', { summary: Constants.per.guest })
|
||||||
|
public async loginBySms(
|
||||||
|
@Body(ALL)
|
||||||
|
body: any
|
||||||
|
) {
|
||||||
|
const token = await this.loginService.loginBySmsCode(body);
|
||||||
|
|
||||||
|
this.ctx.cookies.set('token', token.token, {
|
||||||
|
maxAge: 1000 * token.expire,
|
||||||
|
});
|
||||||
|
|
||||||
|
return this.ok(token);
|
||||||
|
}
|
||||||
|
|
||||||
@Post('/logout', { summary: Constants.per.authOnly })
|
@Post('/logout', { summary: Constants.per.authOnly })
|
||||||
public logout() {}
|
public logout() {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,8 @@ import { RoleService } from '../../sys/authority/service/role-service.js';
|
||||||
import { UserEntity } from '../../sys/authority/entity/user.js';
|
import { UserEntity } from '../../sys/authority/entity/user.js';
|
||||||
import { SysSettingsService } from '@certd/lib-server';
|
import { SysSettingsService } from '@certd/lib-server';
|
||||||
import { SysPrivateSettings } from '@certd/lib-server';
|
import { SysPrivateSettings } from '@certd/lib-server';
|
||||||
|
import { cache } from '@certd/basic';
|
||||||
|
import { LoginErrorException } from '@certd/lib-server/dist/basic/exception/login-error-exception.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 系统用户
|
* 系统用户
|
||||||
|
@ -22,6 +24,76 @@ export class LoginService {
|
||||||
@Inject()
|
@Inject()
|
||||||
sysSettingsService: SysSettingsService;
|
sysSettingsService: SysSettingsService;
|
||||||
|
|
||||||
|
checkErrorTimes(username: string, errorMessage: string) {
|
||||||
|
const cacheKey = `login_error_times:${username}`;
|
||||||
|
const blockTimesKey = `login_block_times:${username}`;
|
||||||
|
let blockTimes = cache.get(blockTimesKey);
|
||||||
|
let maxWaitMin = 2;
|
||||||
|
const maxRetryTimes = 5;
|
||||||
|
if (blockTimes == null) {
|
||||||
|
blockTimes = 1;
|
||||||
|
}
|
||||||
|
maxWaitMin = maxWaitMin * blockTimes;
|
||||||
|
let ttl = maxWaitMin * 60 * 1000;
|
||||||
|
|
||||||
|
let errorTimes = cache.get(cacheKey);
|
||||||
|
|
||||||
|
if (errorTimes == null) {
|
||||||
|
errorTimes = 0;
|
||||||
|
} else {
|
||||||
|
const remainingTTL = cache.getRemainingTTL(cacheKey);
|
||||||
|
if (remainingTTL > 0) {
|
||||||
|
ttl = remainingTTL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
errorTimes += 1;
|
||||||
|
|
||||||
|
cache.set(cacheKey, errorTimes, {
|
||||||
|
ttl: ttl,
|
||||||
|
});
|
||||||
|
if (errorTimes >= maxRetryTimes) {
|
||||||
|
if (errorTimes === maxRetryTimes) {
|
||||||
|
blockTimes += 1;
|
||||||
|
cache.set(blockTimesKey, blockTimes, {
|
||||||
|
ttl: 24 * 60 * 60 * 1000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const leftMin = Math.ceil(ttl / 1000 / 60);
|
||||||
|
throw new LoginErrorException(`登录失败次数过多,请${leftMin}分钟后重试`, 0);
|
||||||
|
}
|
||||||
|
const leftTimes = maxRetryTimes - errorTimes;
|
||||||
|
if (leftTimes < 3) {
|
||||||
|
throw new LoginErrorException(`登录失败,剩余尝试次数:${leftTimes}`, leftTimes);
|
||||||
|
}
|
||||||
|
throw new LoginErrorException(errorMessage, leftTimes);
|
||||||
|
}
|
||||||
|
|
||||||
|
async loginBySmsCode(req: { mobile: string; phoneCode: string; smsChecked: boolean }) {
|
||||||
|
const { mobile, phoneCode, smsChecked } = req;
|
||||||
|
if (!smsChecked) {
|
||||||
|
this.checkErrorTimes(mobile, '验证码错误');
|
||||||
|
}
|
||||||
|
const info = await this.userService.findOne({ phoneCode, mobile: mobile });
|
||||||
|
if (info == null) {
|
||||||
|
throw new CommonException('手机号或验证码错误');
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.onLoginSuccess(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
async loginByPassword(req: { username: string; password: string; phoneCode: string }) {
|
||||||
|
const { username, password, phoneCode } = req;
|
||||||
|
const info = await this.userService.findOne([{ username: username }, { email: username }, { phoneCode, mobile: username }]);
|
||||||
|
if (info == null) {
|
||||||
|
throw new CommonException('用户名或密码错误');
|
||||||
|
}
|
||||||
|
const right = await this.userService.checkPassword(password, info.password, info.passwordVersion);
|
||||||
|
if (!right) {
|
||||||
|
this.checkErrorTimes(username, '用户名或密码错误');
|
||||||
|
}
|
||||||
|
return this.onLoginSuccess(info);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* login
|
* login
|
||||||
*/
|
*/
|
||||||
|
@ -33,12 +105,15 @@ export class LoginService {
|
||||||
}
|
}
|
||||||
const right = await this.userService.checkPassword(user.password, info.password, info.passwordVersion);
|
const right = await this.userService.checkPassword(user.password, info.password, info.passwordVersion);
|
||||||
if (!right) {
|
if (!right) {
|
||||||
throw new CommonException('用户名或密码错误');
|
this.checkErrorTimes(user.username, '用户名或密码错误');
|
||||||
}
|
}
|
||||||
|
return await this.onLoginSuccess(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async onLoginSuccess(info: UserEntity) {
|
||||||
if (info.status === 0) {
|
if (info.status === 0) {
|
||||||
throw new CommonException('用户已被禁用');
|
throw new CommonException('用户已被禁用');
|
||||||
}
|
}
|
||||||
|
|
||||||
const roleIds = await this.roleService.getRoleIdsByUserId(info.id);
|
const roleIds = await this.roleService.getRoleIdsByUserId(info.id);
|
||||||
return this.generateToken(info, roleIds);
|
return this.generateToken(info, roleIds);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue