import { InjectRedis } from '@liaoliaots/nestjs-redis'; import { Injectable } from '@nestjs/common'; import Redis from 'ioredis'; import { isEmpty } from 'lodash'; import { BusinessException } from '~/common/exceptions/biz.exception'; import { ErrorEnum } from '~/constants/error-code.constant'; import { genAuthPVKey, genAuthPermKey, genAuthTokenKey } from '~/helper/genRedisKey'; import { UserService } from '~/modules/user/user.service'; import { md5 } from '~/utils'; import { LoginLogService } from '../system/log/services/login-log.service'; import { MenuService } from '../system/menu/menu.service'; import { RoleService } from '../system/role/role.service'; import { TokenService } from './services/token.service'; @Injectable() export class AuthService { constructor( @InjectRedis() private readonly redis: Redis, private menuService: MenuService, private roleService: RoleService, private userService: UserService, private loginLogService: LoginLogService, private tokenService: TokenService ) {} async validateUser(credential: string, password: string): Promise { const user = await this.userService.findUserByUserName(credential); if (isEmpty(user)) throw new BusinessException(ErrorEnum.USER_NOT_FOUND); const comparePassword = md5(`${password}${user.psalt}`); if (user.password !== comparePassword) throw new BusinessException(ErrorEnum.INVALID_USERNAME_PASSWORD); if (user) { const { password, ...result } = user; return result; } return null; } /** * 获取登录JWT * 返回null则账号密码有误,不存在该用户 */ async login(username: string, password: string, ip: string, ua: string): Promise { const user = await this.userService.findUserByUserName(username); if (isEmpty(user)) throw new BusinessException(ErrorEnum.INVALID_USERNAME_PASSWORD); const comparePassword = md5(`${password}${user.psalt}`); if (user.password !== comparePassword) throw new BusinessException(ErrorEnum.INVALID_USERNAME_PASSWORD); const roleIds = await this.roleService.getRoleIdsByUser(user.id); const roles = await this.roleService.getRoleValues(roleIds); // 包含access_token和refresh_token const token = await this.tokenService.generateAccessToken(user.id, roles); await this.redis.set(genAuthTokenKey(user.id), token.accessToken); // 设置密码版本号 当密码修改时,版本号+1 await this.redis.set(genAuthPVKey(user.id), 1); // 设置菜单权限 const permissions = await this.menuService.getPermissions(user.id); await this.setPermissionsCache(user.id, permissions); await this.loginLogService.create(user.id, ip, ua); return token.accessToken; } /** * 解锁屏幕 * 返回null则账号密码有误,不存在该用户 */ async unlock(uid: number, password: string): Promise { const user = await this.userService.findUserById(uid); if (isEmpty(user)) throw new BusinessException(ErrorEnum.INVALID_USERNAME_PASSWORD); const comparePassword = md5(`${password}${user.psalt}`); if (user.password !== comparePassword) throw new BusinessException(ErrorEnum.INVALID_USERNAME_PASSWORD); } /** * 效验账号密码 */ async checkPassword(username: string, password: string) { const user = await this.userService.findUserByUserName(username); const comparePassword = md5(`${password}${user.psalt}`); if (user.password !== comparePassword) throw new BusinessException(ErrorEnum.INVALID_USERNAME_PASSWORD); } async loginLog(uid: number, ip: string, ua: string) { await this.loginLogService.create(uid, ip, ua); } async logout(uid: number) { // 删除token await this.userService.forbidden(uid); } /** * 重置密码 */ async resetPassword(username: string, password: string) { const user = await this.userService.findUserByUserName(username); await this.userService.forceUpdatePassword(user.id, password); } /** * 清除登录状态信息 */ async clearLoginStatus(uid: number): Promise { await this.userService.forbidden(uid); } /** * 获取菜单列表 */ async getMenus(uid: number): Promise { return this.menuService.getMenus(uid); } /** * 获取权限列表 */ async getPermissions(uid: number): Promise { return this.menuService.getPermissions(uid); } async getPermissionsCache(uid: number): Promise { const permissionString = await this.redis.get(genAuthPermKey(uid)); return permissionString ? JSON.parse(permissionString) : []; } async setPermissionsCache(uid: number, permissions: string[]): Promise { await this.redis.set(genAuthPermKey(uid), JSON.stringify(permissions)); } async getPasswordVersionByUid(uid: number): Promise { return this.redis.get(genAuthPVKey(uid)); } async getTokenByUid(uid: number): Promise { return this.redis.get(genAuthTokenKey(uid)); } }