2025-01-15 01:05:34 +08:00
|
|
|
import { Config, Inject, Provide, Scope, ScopeEnum } from '@midwayjs/core';
|
2024-10-07 03:21:16 +08:00
|
|
|
import { UserService } from '../../sys/authority/service/user-service.js';
|
2024-07-15 00:30:33 +08:00
|
|
|
import jwt from 'jsonwebtoken';
|
2024-10-03 22:03:49 +08:00
|
|
|
import { CommonException } from '@certd/lib-server';
|
2024-10-07 03:21:16 +08:00
|
|
|
import { RoleService } from '../../sys/authority/service/role-service.js';
|
|
|
|
|
import { UserEntity } from '../../sys/authority/entity/user.js';
|
2024-10-03 22:03:49 +08:00
|
|
|
import { SysSettingsService } from '@certd/lib-server';
|
|
|
|
|
import { SysPrivateSettings } from '@certd/lib-server';
|
2024-11-28 11:10:57 +08:00
|
|
|
import { cache } from '@certd/basic';
|
|
|
|
|
import { LoginErrorException } from '@certd/lib-server/dist/basic/exception/login-error-exception.js';
|
2024-11-28 17:36:45 +08:00
|
|
|
import { CodeService } from '../../basic/service/code-service.js';
|
2023-01-29 13:44:19 +08:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 系统用户
|
|
|
|
|
*/
|
|
|
|
|
@Provide()
|
2025-01-15 01:05:34 +08:00
|
|
|
@Scope(ScopeEnum.Request, { allowDowngrade: true })
|
2023-01-29 13:44:19 +08:00
|
|
|
export class LoginService {
|
|
|
|
|
@Inject()
|
|
|
|
|
userService: UserService;
|
2023-06-27 09:29:43 +08:00
|
|
|
@Inject()
|
|
|
|
|
roleService: RoleService;
|
2024-11-28 17:36:45 +08:00
|
|
|
|
|
|
|
|
@Inject()
|
|
|
|
|
codeService: CodeService;
|
2023-06-28 09:44:35 +08:00
|
|
|
@Config('auth.jwt')
|
2023-01-29 13:44:19 +08:00
|
|
|
private jwt: any;
|
|
|
|
|
|
2024-08-27 13:46:19 +08:00
|
|
|
@Inject()
|
|
|
|
|
sysSettingsService: SysSettingsService;
|
|
|
|
|
|
2024-11-28 11:10:57 +08:00
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-28 17:36:45 +08:00
|
|
|
async loginBySmsCode(req: { mobile: string; phoneCode: string; smsCode: string; randomStr: string }) {
|
|
|
|
|
const smsChecked = await this.codeService.checkSmsCode({
|
|
|
|
|
mobile: req.mobile,
|
|
|
|
|
phoneCode: req.phoneCode,
|
|
|
|
|
smsCode: req.smsCode,
|
|
|
|
|
randomStr: req.randomStr,
|
|
|
|
|
throwError: false,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const { mobile, phoneCode } = req;
|
2024-11-28 11:10:57 +08:00
|
|
|
if (!smsChecked) {
|
|
|
|
|
this.checkErrorTimes(mobile, '验证码错误');
|
|
|
|
|
}
|
2024-11-28 17:36:45 +08:00
|
|
|
let info = await this.userService.findOne({ phoneCode, mobile: mobile });
|
2024-11-28 11:10:57 +08:00
|
|
|
if (info == null) {
|
2024-11-28 17:36:45 +08:00
|
|
|
//用户不存在,注册
|
|
|
|
|
info = await this.userService.register('mobile', {
|
|
|
|
|
phoneCode,
|
|
|
|
|
mobile,
|
|
|
|
|
password: '',
|
|
|
|
|
} as any);
|
2024-11-28 11:10:57 +08:00
|
|
|
}
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-28 17:36:45 +08:00
|
|
|
// /**
|
|
|
|
|
// * login
|
|
|
|
|
// */
|
|
|
|
|
// async login(user) {
|
|
|
|
|
// console.assert(user.username != null, '用户名不能为空');
|
|
|
|
|
// const info = await this.userService.findOne({ username: user.username });
|
|
|
|
|
// if (info == null) {
|
|
|
|
|
// throw new CommonException('用户名或密码错误');
|
|
|
|
|
// }
|
|
|
|
|
// const right = await this.userService.checkPassword(user.password, info.password, info.passwordVersion);
|
|
|
|
|
// if (!right) {
|
|
|
|
|
// this.checkErrorTimes(user.username, '用户名或密码错误');
|
|
|
|
|
// }
|
|
|
|
|
// return await this.onLoginSuccess(info);
|
|
|
|
|
// }
|
2024-11-28 11:10:57 +08:00
|
|
|
|
|
|
|
|
private async onLoginSuccess(info: UserEntity) {
|
2024-10-27 00:04:02 +08:00
|
|
|
if (info.status === 0) {
|
|
|
|
|
throw new CommonException('用户已被禁用');
|
|
|
|
|
}
|
2023-06-27 09:29:43 +08:00
|
|
|
const roleIds = await this.roleService.getRoleIdsByUserId(info.id);
|
|
|
|
|
return this.generateToken(info, roleIds);
|
2023-01-29 13:44:19 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 生成token
|
|
|
|
|
* @param user 用户对象
|
2023-06-27 09:29:43 +08:00
|
|
|
* @param roleIds
|
2023-01-29 13:44:19 +08:00
|
|
|
*/
|
2023-06-27 09:29:43 +08:00
|
|
|
async generateToken(user: UserEntity, roleIds: number[]) {
|
2023-01-29 13:44:19 +08:00
|
|
|
const tokenInfo = {
|
|
|
|
|
username: user.username,
|
|
|
|
|
id: user.id,
|
2023-06-27 09:29:43 +08:00
|
|
|
roles: roleIds,
|
2023-01-29 13:44:19 +08:00
|
|
|
};
|
|
|
|
|
const expire = this.jwt.expire;
|
2024-08-27 13:46:19 +08:00
|
|
|
|
|
|
|
|
const setting = await this.sysSettingsService.getSetting<SysPrivateSettings>(SysPrivateSettings);
|
|
|
|
|
const jwtSecret = setting.jwtKey;
|
|
|
|
|
|
|
|
|
|
const token = jwt.sign(tokenInfo, jwtSecret, {
|
2023-01-29 13:44:19 +08:00
|
|
|
expiresIn: expire,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
token,
|
|
|
|
|
expire,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|