login.service.ts 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. import { Config, Inject, Provide } from '@midwayjs/core';
  2. import { FrameworkErrorEnum, GetModel, ServiceError } from 'free-midway-component';
  3. import { get, isEqual, upperFirst } from 'lodash';
  4. import { LoginDTO, LoginType, UPwdDTO } from '../interface/login.interface';
  5. import { RoleService } from '../service/system/role.service';
  6. import { RedisService } from '@midwayjs/redis';
  7. import * as Crypto from 'crypto-js';
  8. import { Context } from '@midwayjs/koa';
  9. import { I18nService } from './i18n.service';
  10. @Provide()
  11. export class LoginService {
  12. @Inject()
  13. roleService: RoleService;
  14. @Inject()
  15. ctx: Context;
  16. @Config('loginSign')
  17. loginSign;
  18. @Config('jwt.secret')
  19. jwtSecret;
  20. @Config('jwt.expiresIn')
  21. jwtExpiresIn;
  22. @Inject()
  23. redisService: RedisService;
  24. @Inject()
  25. i18n: I18nService;
  26. async onePointLogin(loginVo) {
  27. const { _id, role } = loginVo;
  28. const rediskey = `${this.loginSign}:${role}:${_id}`;
  29. // 随机字符串 验证用户登录用
  30. const str = this.randomStr();
  31. // 拼接成token内的登录code 加密前数据
  32. const val = `${rediskey}:${str}`;
  33. // 加密后存入token内
  34. const code = Crypto.AES.encrypt(val, this.jwtSecret).toString();
  35. // 设置redis登录记录 将 随机字符串存在redis中. 验证时需要将加密的字符串解密,解密后和redis中的对比验证
  36. await this.redisService.set(rediskey, str, 'EX', this.jwtExpiresIn);
  37. return code;
  38. }
  39. async onePointCheck() {
  40. const user = this.ctx.user;
  41. if (!user) throw new ServiceError(this.i18n.translateError(FrameworkErrorEnum.NOT_LOGIN), FrameworkErrorEnum.NOT_LOGIN);
  42. const { _id, role, login_code } = user;
  43. if (!login_code) throw new ServiceError(this.i18n.translateError(FrameworkErrorEnum.NOT_LOGIN), FrameworkErrorEnum.NOT_LOGIN);
  44. // 解密
  45. const decodeResult = Crypto.AES.decrypt(login_code, this.jwtSecret);
  46. const decode = Crypto.enc.Utf8.stringify(decodeResult).toString();
  47. // 取出code
  48. const codeArr = decode.split(':');
  49. const code = codeArr[codeArr.length - 1];
  50. // 拼接redis的key
  51. const rediskey = `${this.loginSign}:${role}:${_id}`;
  52. // 取出当前记录在案的 code
  53. const redisCode = await this.redisService.get(rediskey);
  54. if (!redisCode) throw new ServiceError(this.i18n.translateError(`${FrameworkErrorEnum.NOT_LOGIN}-1`), FrameworkErrorEnum.NOT_LOGIN);
  55. // 判断是否一致
  56. if (code === redisCode) {
  57. // 一致,延时
  58. await this.redisService.expire(rediskey, this.jwtExpiresIn);
  59. } else {
  60. throw new ServiceError(this.i18n.translateError(`${FrameworkErrorEnum.NOT_LOGIN}-2`), FrameworkErrorEnum.NOT_LOGIN);
  61. }
  62. }
  63. /**
  64. * 账密登录
  65. * @param data 用户名和密码
  66. * @param type 用户类型
  67. * @returns 用户信息/空值
  68. */
  69. async loginByAccount(data: LoginDTO, type: LoginType) {
  70. const model = GetModel(upperFirst(type));
  71. const user = await model.findOne({ account: data.account }, '+password').lean();
  72. if (!user) throw new ServiceError('未找到用户信息', FrameworkErrorEnum.NOT_FOUND_DATA);
  73. await this.checkAccountCanLogin(user, type);
  74. if (!isEqual(user.password.secret, data.password)) throw new ServiceError('密码错误', FrameworkErrorEnum.SERVICE_FAULT);
  75. return user;
  76. }
  77. /**
  78. * 检查用户是否可以登录(从用户本身和角色检查)
  79. * @param user 用户信息
  80. * @param type 用户类型
  81. */
  82. async checkAccountCanLogin(user, type: LoginType) {
  83. // 判断是否可以使用:
  84. // 管理员: 超级管理员无视,直接往下; 普通管理员需要查看is_use是不是'0'
  85. // 其他用户需要看status是不是'1';
  86. if (type === 'Admin') {
  87. if (get(user, 'is_super') === '1') {
  88. if (get(user, 'is_use') === '1') throw new ServiceError('该用户已被禁用', FrameworkErrorEnum.SERVICE_FAULT);
  89. }
  90. } else {
  91. if (get(user, 'status') !== '1') throw new ServiceError('该用户已被禁用', FrameworkErrorEnum.SERVICE_FAULT);
  92. const role = await this.roleService.findOne({ code: type, is_use: '0' });
  93. if (!role) throw new ServiceError('当前角色下的用户无法使用!', FrameworkErrorEnum.SERVICE_FAULT);
  94. }
  95. }
  96. async updatePwd(data: UPwdDTO, type: LoginType) {
  97. const model = GetModel(upperFirst(type));
  98. const user = await model.findById(data._id);
  99. if (!user) new ServiceError('未找到用户信息!', FrameworkErrorEnum.DATA_NOT_FOUND);
  100. user.password = data.password;
  101. await user.save();
  102. }
  103. // 需要改的时候再用
  104. // async wxAppLogin(openid: string) {
  105. // const tables = ['Doctor', 'Nurse', 'Patient'];
  106. // let user;
  107. // let role;
  108. // for (const table of tables) {
  109. // const model = GetModel(upperFirst(table));
  110. // user = await model.findOne({ openid }).lean();
  111. // if (user) {
  112. // role = table;
  113. // break;
  114. // }
  115. // }
  116. // if (user) return { ...user, role };
  117. // throw new ServiceError('未找到用户信息!', FrameworkErrorEnum.NOT_FOUND_DATA);
  118. // }
  119. randomStr(len = 32) {
  120. return Math.random().toString(36).slice(-len);
  121. }
  122. }