import { Config, Inject, Provide } from '@midwayjs/core'; import { get } from 'lodash'; import { RoleService } from '../service/system/role.service'; import { RedisService } from '@midwayjs/redis'; import * as Crypto from 'crypto-js'; import { Context } from '@midwayjs/koa'; import { ServiceError, ErrorCode } from '../error/service.error'; import { InjectEntityModel } from '@midwayjs/typeorm'; import { Equal, Repository } from 'typeorm'; import { LoginDTO, LoginType, UPwdDTO } from '../interface/login.interface'; import { Opera } from '../frame/dbOpera'; import { Admin } from '../entity/system/admin.entity'; import * as bcrypt from 'bcryptjs'; import { User } from '../entity/system/user.entity'; @Provide() export class LoginService { @Inject() roleService: RoleService; @Inject() ctx: Context; @Config('loginSign') loginSign; @Config('jwt.secret') jwtSecret; @Config('jwt.expiresIn') jwtExpiresIn; @Inject() redisService: RedisService; @InjectEntityModel(Admin) adminModel: Repository; @InjectEntityModel(User) userModel: Repository; async onePointLogin(loginVo) { const { id, role } = loginVo; const rediskey = `${this.loginSign}:${role}:${id}`; // 随机字符串 验证用户登录用 const str = this.randomStr(); // 拼接成token内的登录code 加密前数据 const val = `${rediskey}:${str}`; // 加密后存入token内 const code = Crypto.AES.encrypt(val, this.jwtSecret).toString(); // 设置redis登录记录 将 随机字符串存在redis中. 验证时需要将加密的字符串解密,解密后和redis中的对比验证 await this.redisService.set(rediskey, str, 'EX', this.jwtExpiresIn); return code; } /** * 账密登录 * @param data 用户名和密码 * @param type 用户类型 * @returns 用户信息/空值 */ async loginByAccount(data: LoginDTO, type: LoginType) { let model; if (type === LoginType.Admin) model = this.adminModel; else model = this.userModel; const user = await model.createQueryBuilder('t').where('t.account = :account', { account: data.account }).addSelect('t.password').getOne(); if (!user) throw new ServiceError(ErrorCode.USER_NOT_FOUND); await this.checkAccountCanLogin(user, type); const result = bcrypt.compareSync(data.password, user.password); if (!result) throw new ServiceError(ErrorCode.BAD_PASSWORD); return user; } /** * 检查用户是否可以登录(从用户本身和角色检查) * @param user 用户信息 * @param type 用户类型 */ async checkAccountCanLogin(user, type: LoginType) { // 判断是否可以使用: // 管理员: 超级管理员无视,直接往下; 普通管理员需要查看is_use是不是'0' // 其他用户需要看status是不是'1'; if (type === 'Admin') { if (get(user, 'is_super') === '1') { if (get(user, 'is_use') === '1') throw new ServiceError(ErrorCode.USER_IS_DISABLED); } } else { if (get(user, 'status') !== '1') throw new ServiceError(ErrorCode.USER_IS_DISABLED); const qb = [ { column: 'code', opera: [Opera.Equal], value: type }, { column: 'is_use', opera: [Opera.Equal], value: '0' }, ]; const role = await this.roleService.fetch(qb); if (!role) throw new ServiceError(ErrorCode.ROLE_IS_DISABLED); } } // 修改密码 async updatePwd(data: UPwdDTO, type: LoginType) { let model; if (type === LoginType.Admin) model = this.adminModel; else model = this.userModel; const user = await model.findOne({ where: { id: Equal(data.id) } }); if (!user) new ServiceError(ErrorCode.USER_NOT_FOUND); await model.update({ id: data.id }, { password: data.password }); } // 需要改的时候再用 async wxAppLogin(openid: string, type: LoginType) { let model; if (type === LoginType.Admin) model = this.adminModel; else model = this.userModel; const user = await model.findOne({ where: { openid: Equal(openid) } }); if (!user) throw new ServiceError(ErrorCode.USER_NOT_FOUND); return user; } randomStr(len = 32) { return Math.random().toString(36).slice(-len); } }