sys_login.py 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. # -*- coding: utf-8 -*-
  2. import datetime
  3. from flask import Request, request
  4. from ruoyi_common.constant import Constants
  5. from ruoyi_common.domain.vo import LoginBody
  6. from ruoyi_common.utils import AddressUtil, IpUtil, MessageUtil
  7. from ruoyi_common.utils import security_util as SecurityUtil
  8. from ruoyi_common.domain.entity import LoginUser, SysUser
  9. from ruoyi_common.domain.enum import UserStatus
  10. from ruoyi_common.exception import ServiceException
  11. from ruoyi_common.utils.base import UserAgentUtil
  12. from ruoyi_framework.asyncsched.manager import TaskManager
  13. from ruoyi_framework.asyncsched.task import record_logininfor
  14. from ruoyi_system.domain.entity import SysLogininfor
  15. from ruoyi_system.service import SysConfigService,SysUserService
  16. from ruoyi_admin.ext import lm,redis_cache
  17. from .token import TokenService
  18. from .sys_permission import SysPermissionService
  19. class LoginService:
  20. @classmethod
  21. def login(cls, login: LoginBody) -> str:
  22. """
  23. 登录
  24. Args:
  25. login (LoginBody): 登录参数
  26. Raises:
  27. ServiceException: 登录失败
  28. ServiceException: 验证码错误
  29. Returns:
  30. str: 登录token
  31. """
  32. captcha_on_off = SysConfigService.select_captcha_on_off()
  33. if captcha_on_off:
  34. cls.validate_captcha(login.username, login.code, login.uuid)
  35. sysuser = SysUserService.select_user_by_user_name(login.username)
  36. if not sysuser:
  37. logininfor = cls.build_logininfor(
  38. login.username,
  39. Constants.LOGIN_FAIL,
  40. MessageUtil.message(code="user.password.not.match")
  41. )
  42. TaskManager.execute(record_logininfor,logininfor)
  43. raise ServiceException(f"登录用户:{login.username} 不存在")
  44. if not SecurityUtil.matches_password(login.password.get_secret_value(), sysuser.password):
  45. logininfor = cls.build_logininfor(
  46. login.username,
  47. Constants.LOGIN_FAIL,
  48. MessageUtil.message(code="user.password.not.match")
  49. )
  50. TaskManager.execute(record_logininfor,logininfor)
  51. raise ServiceException("用户名或密码不匹配")
  52. logininfor = cls.build_logininfor(
  53. login.username,
  54. Constants.LOGIN_SUCCESS,
  55. MessageUtil.message(code="user.login.success")
  56. )
  57. TaskManager.execute(record_logininfor,logininfor)
  58. login_user = cls.load_user(sysuser)
  59. cls.record_sysuser(login_user.user_id)
  60. return TokenService.create_token(login_user)
  61. @classmethod
  62. def load_user(cls, user:SysUser) -> LoginUser:
  63. """
  64. 加载登录用户
  65. Args:
  66. user (SysUser): 用户信息
  67. Raises:
  68. ServiceException: 用户已删除
  69. ServiceException: 用户已停用
  70. Returns:
  71. LoginUser: 登录用户
  72. """
  73. if user.del_flag == UserStatus.DELETED.value:
  74. raise ServiceException(f"对不起,您的账号:{user.user_name} 已删除")
  75. if user.status == UserStatus.DISABLE.value:
  76. raise ServiceException(f"对不起,您的账号:{user.user_name} 已停用")
  77. login_user = LoginUser(
  78. user_id=user.user_id,
  79. dept_id=user.dept_id,
  80. permissions=SysPermissionService.get_menu_permission(user),
  81. user=user,
  82. )
  83. return login_user
  84. @classmethod
  85. def validate_captcha(cls, username:str, code:str, uuid:str):
  86. """
  87. 验证码校验
  88. Args:
  89. username (str): 用户名
  90. code (str): 验证码
  91. uuid (str): 验证码唯一标识
  92. Raises:
  93. ServiceException: 验证码无效
  94. ServiceException: 验证码过期
  95. """
  96. verify_key = Constants.CAPTCHA_CODE_KEY + str(uuid)
  97. captcha:bytes = redis_cache.get(verify_key)
  98. if not captcha:
  99. logininfor = cls.build_logininfor(
  100. username,
  101. Constants.LOGIN_FAIL,
  102. MessageUtil.message("user.jcaptcha.expire")
  103. )
  104. TaskManager.execute(record_logininfor,logininfor)
  105. raise ServiceException("验证码无效")
  106. redis_cache.delete(verify_key)
  107. if captcha.decode("utf-8").lower() != code.lower():
  108. logininfor = cls.build_logininfor(
  109. username,
  110. Constants.LOGIN_FAIL,
  111. MessageUtil.message("user.jcaptcha.error")
  112. )
  113. TaskManager.execute(record_logininfor,logininfor)
  114. raise ServiceException("验证码错误")
  115. @classmethod
  116. def record_sysuser(cls, id: int):
  117. """
  118. 记录登录信息
  119. Args:
  120. id (int): 用户id
  121. """
  122. sysuser = SysUser(
  123. user_id=id,
  124. login_ip=IpUtil.get_ip(),
  125. login_date=datetime.datetime.now()
  126. )
  127. SysUserService.update_user(sysuser)
  128. @classmethod
  129. def build_logininfor(cls, username:str,status:str,message:str) -> SysLogininfor:
  130. """
  131. 构建登录信息
  132. Args:
  133. username (str): 用户名
  134. status (str): 登录状态
  135. message (str): 登录信息
  136. Returns:
  137. SysLogininfor: 登录信息
  138. """
  139. ip = IpUtil.get_ip()
  140. address = AddressUtil.get_address(ip)
  141. browser = UserAgentUtil.browser()
  142. os = UserAgentUtil.os()
  143. logininfor = SysLogininfor(
  144. user_name=username,
  145. ipaddr=ip,
  146. login_location=address,
  147. browser=browser,
  148. os=os,
  149. msg=message,
  150. login_time=datetime.datetime.now()
  151. )
  152. if status in [Constants.LOGIN_SUCCESS, Constants.LOGOUT, Constants.REGISTER]:
  153. logininfor.status = Constants.SUCCESS
  154. else:
  155. logininfor.status = Constants.FAIL
  156. return logininfor
  157. @classmethod
  158. def logout(cls) -> bool:
  159. '''
  160. 退出登录
  161. Returns:
  162. bool: 是否退出成功
  163. '''
  164. login_user:LoginUser = TokenService.get_login_user(request=request)
  165. if login_user:
  166. TokenService.del_login_user(token=login_user.token.hex)
  167. logininfor = cls.build_logininfor(
  168. login_user.user_name,
  169. Constants.LOGIN_SUCCESS,
  170. "退出成功")
  171. TaskManager.execute(record_logininfor,logininfor)
  172. return True
  173. @lm.unauthorized_handler
  174. def unauthorized_handler() -> tuple[str, int]:
  175. """
  176. 未授权处理
  177. Returns:
  178. tuple[str, int]: 返回401状态码
  179. """
  180. return "Unauthorized", 401
  181. @lm.request_loader
  182. def load_request(request:Request) -> LoginUser:
  183. """
  184. 加载请求
  185. Args:
  186. request (Request): 请求
  187. Returns:
  188. LoginUser: 登录用户
  189. """
  190. login_user = TokenService.get_login_user(request)
  191. return login_user