login.service.ts 4.9 KB

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