SysAuthService.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  1. // 大名科技(天津)有限公司版权所有 电话:18020030720 QQ:515096995
  2. //
  3. // 此源代码遵循位于源代码树根目录中的 LICENSE 文件的许可证
  4. using Furion.SpecificationDocument;
  5. using Lazy.Captcha.Core;
  6. namespace Admin.NET.Core.Service;
  7. /// <summary>
  8. /// 系统登录授权服务
  9. /// </summary>
  10. [ApiDescriptionSettings(Order = 500)]
  11. public class SysAuthService : IDynamicApiController, ITransient
  12. {
  13. private readonly UserManager _userManager;
  14. private readonly SqlSugarRepository<SysUser> _sysUserRep;
  15. private readonly IHttpContextAccessor _httpContextAccessor;
  16. private readonly SysMenuService _sysMenuService;
  17. private readonly SysOnlineUserService _sysOnlineUserService;
  18. private readonly SysConfigService _sysConfigService;
  19. private readonly ICaptcha _captcha;
  20. private readonly SysCacheService _sysCacheService;
  21. public SysAuthService(UserManager userManager,
  22. SqlSugarRepository<SysUser> sysUserRep,
  23. IHttpContextAccessor httpContextAccessor,
  24. SysMenuService sysMenuService,
  25. SysOnlineUserService sysOnlineUserService,
  26. SysConfigService sysConfigService,
  27. ICaptcha captcha,
  28. SysCacheService sysCacheService)
  29. {
  30. _userManager = userManager;
  31. _sysUserRep = sysUserRep;
  32. _httpContextAccessor = httpContextAccessor;
  33. _sysMenuService = sysMenuService;
  34. _sysOnlineUserService = sysOnlineUserService;
  35. _sysConfigService = sysConfigService;
  36. _captcha = captcha;
  37. _sysCacheService = sysCacheService;
  38. }
  39. /// <summary>
  40. /// 账号密码登录
  41. /// </summary>
  42. /// <param name="input"></param>
  43. /// <remarks>用户名/密码:superadmin/123456</remarks>
  44. /// <returns></returns>
  45. [AllowAnonymous]
  46. [DisplayName("账号密码登录")]
  47. public async Task<LoginOutput> Login([Required] LoginInput input)
  48. {
  49. //// 可以根据域名获取具体租户
  50. //var host = _httpContextAccessor.HttpContext.Request.Host;
  51. // 判断密码错误次数(默认5次,缓存30分钟)
  52. var keyErrorPasswordCount = $"{CacheConst.KeyErrorPasswordCount}{input.Account}";
  53. var errorPasswordCount = _sysCacheService.Get<int>(keyErrorPasswordCount);
  54. if (errorPasswordCount >= 5)
  55. throw Oops.Oh(ErrorCodeEnum.D1027);
  56. // 是否开启验证码
  57. if (await _sysConfigService.GetConfigValue<bool>(CommonConst.SysCaptcha))
  58. {
  59. // 判断验证码
  60. if (!_captcha.Validate(input.CodeId.ToString(), input.Code))
  61. throw Oops.Oh(ErrorCodeEnum.D0008);
  62. }
  63. // 账号是否存在
  64. var user = await _sysUserRep.AsQueryable().Includes(t => t.SysOrg).ClearFilter().FirstAsync(u => u.Account.Equals(input.Account));
  65. _ = user ?? throw Oops.Oh(ErrorCodeEnum.D0009);
  66. // 账号是否被冻结
  67. if (user.Status == StatusEnum.Disable)
  68. throw Oops.Oh(ErrorCodeEnum.D1017);
  69. // 租户是否被禁用
  70. var tenant = await _sysUserRep.ChangeRepository<SqlSugarRepository<SysTenant>>().GetFirstAsync(u => u.Id == user.TenantId);
  71. if (tenant != null && tenant.Status == StatusEnum.Disable)
  72. throw Oops.Oh(ErrorCodeEnum.Z1003);
  73. // 国密SM2解密(前端密码传输SM2加密后的)
  74. input.Password = CryptogramUtil.SM2Decrypt(input.Password);
  75. // 密码是否正确
  76. if (CryptogramUtil.CryptoType == CryptogramEnum.MD5.ToString())
  77. {
  78. if (!user.Password.Equals(MD5Encryption.Encrypt(input.Password)))
  79. {
  80. _sysCacheService.Set(keyErrorPasswordCount, ++errorPasswordCount, TimeSpan.FromMinutes(30));
  81. throw Oops.Oh(ErrorCodeEnum.D1000);
  82. }
  83. }
  84. else
  85. {
  86. if (!CryptogramUtil.Decrypt(user.Password).Equals(input.Password))
  87. {
  88. _sysCacheService.Set(keyErrorPasswordCount, ++errorPasswordCount, TimeSpan.FromMinutes(30));
  89. throw Oops.Oh(ErrorCodeEnum.D1000);
  90. }
  91. }
  92. // 登录成功则清空密码错误次数
  93. _sysCacheService.Remove(keyErrorPasswordCount);
  94. return await CreateToken(user);
  95. }
  96. /// <summary>
  97. /// 验证锁屏密码
  98. /// </summary>
  99. /// <param name="password"></param>
  100. /// <returns></returns>
  101. [DisplayName("验证锁屏密码")]
  102. public async Task<bool> UnLockScreen([Required, FromQuery] string password)
  103. {
  104. // 账号是否存在
  105. var user = await _sysUserRep.GetFirstAsync(u => u.Id == _userManager.UserId);
  106. _ = user ?? throw Oops.Oh(ErrorCodeEnum.D0009);
  107. // 国密SM2解密(前端密码传输SM2加密后的)
  108. password = CryptogramUtil.SM2Decrypt(password);
  109. // 密码是否正确
  110. if (CryptogramUtil.CryptoType == CryptogramEnum.MD5.ToString())
  111. {
  112. if (!user.Password.Equals(MD5Encryption.Encrypt(password)))
  113. throw Oops.Oh(ErrorCodeEnum.D1000);
  114. }
  115. else
  116. {
  117. if (!CryptogramUtil.Decrypt(user.Password).Equals(password))
  118. throw Oops.Oh(ErrorCodeEnum.D1000);
  119. }
  120. return true;
  121. }
  122. /// <summary>
  123. /// 手机号登录
  124. /// </summary>
  125. /// <param name="input"></param>
  126. /// <returns></returns>
  127. [AllowAnonymous]
  128. [DisplayName("手机号登录")]
  129. public async Task<LoginOutput> LoginPhone([Required] LoginPhoneInput input)
  130. {
  131. var verifyCode = _sysCacheService.Get<string>($"{CacheConst.KeyPhoneVerCode}{input.Phone}");
  132. if (string.IsNullOrWhiteSpace(verifyCode))
  133. throw Oops.Oh("验证码不存在或已失效,请重新获取!");
  134. if (verifyCode != input.Code)
  135. throw Oops.Oh("验证码错误!");
  136. // 账号是否存在
  137. var user = await _sysUserRep.AsQueryable().Includes(t => t.SysOrg).ClearFilter().FirstAsync(u => u.Phone.Equals(input.Phone));
  138. _ = user ?? throw Oops.Oh(ErrorCodeEnum.D0009);
  139. return await CreateToken(user);
  140. }
  141. /// <summary>
  142. /// 生成Token令牌
  143. /// </summary>
  144. /// <param name="user"></param>
  145. /// <returns></returns>
  146. [NonAction]
  147. public async Task<LoginOutput> CreateToken(SysUser user)
  148. {
  149. // 单用户登录
  150. await _sysOnlineUserService.SingleLogin(user.Id);
  151. // 生成Token令牌
  152. var tokenExpire = await _sysConfigService.GetTokenExpire();
  153. var accessToken = JWTEncryption.Encrypt(new Dictionary<string, object>
  154. {
  155. { ClaimConst.UserId, user.Id },
  156. { ClaimConst.TenantId, user.TenantId },
  157. { ClaimConst.Account, user.Account },
  158. { ClaimConst.RealName, user.RealName },
  159. { ClaimConst.AccountType, user.AccountType },
  160. { ClaimConst.OrgId, user.OrgId },
  161. { ClaimConst.OrgName, user.SysOrg?.Name },
  162. { ClaimConst.OrgType, user.SysOrg?.Type },
  163. }, tokenExpire);
  164. // 生成刷新Token令牌
  165. var refreshTokenExpire = await _sysConfigService.GetRefreshTokenExpire();
  166. var refreshToken = JWTEncryption.GenerateRefreshToken(accessToken, refreshTokenExpire);
  167. // 设置响应报文头
  168. _httpContextAccessor.HttpContext.SetTokensOfResponseHeaders(accessToken, refreshToken);
  169. // Swagger Knife4UI-AfterScript登录脚本
  170. // ke.global.setAllHeader('Authorization', 'Bearer ' + ke.response.headers['access-token']);
  171. return new LoginOutput
  172. {
  173. AccessToken = accessToken,
  174. RefreshToken = refreshToken
  175. };
  176. }
  177. /// <summary>
  178. /// 获取登录账号
  179. /// </summary>
  180. /// <returns></returns>
  181. [DisplayName("获取登录账号")]
  182. public async Task<LoginUserOutput> GetUserInfo()
  183. {
  184. var user = await _sysUserRep.GetFirstAsync(u => u.Id == _userManager.UserId) ?? throw Oops.Oh(ErrorCodeEnum.D1011).StatusCode(401);
  185. // 获取机构
  186. var org = await _sysUserRep.ChangeRepository<SqlSugarRepository<SysOrg>>().GetFirstAsync(u => u.Id == user.OrgId);
  187. // 获取职位
  188. var pos = await _sysUserRep.ChangeRepository<SqlSugarRepository<SysPos>>().GetFirstAsync(u => u.Id == user.PosId);
  189. // 获取拥有按钮权限集合
  190. var buttons = await _sysMenuService.GetOwnBtnPermList();
  191. // 获取权限集合
  192. var roleIds = await _sysUserRep.ChangeRepository<SqlSugarRepository<SysUserRole>>().AsQueryable()
  193. .Where(u => u.UserId == user.Id).Select(u => u.RoleId).ToListAsync();
  194. return new LoginUserOutput
  195. {
  196. Id = user.Id,
  197. Account = user.Account,
  198. RealName = user.RealName,
  199. Phone = user.Phone,
  200. IdCardNum = user.IdCardNum,
  201. Email = user.Email,
  202. AccountType = user.AccountType,
  203. Avatar = user.Avatar,
  204. Address = user.Address,
  205. Signature = user.Signature,
  206. OrgId = user.OrgId,
  207. OrgName = org?.Name,
  208. OrgType = org?.Type,
  209. PosName = pos?.Name,
  210. Buttons = buttons,
  211. RoleIds = roleIds
  212. };
  213. }
  214. /// <summary>
  215. /// 获取刷新Token
  216. /// </summary>
  217. /// <param name="accessToken"></param>
  218. /// <returns></returns>
  219. [DisplayName("获取刷新Token")]
  220. public string GetRefreshToken([FromQuery] string accessToken)
  221. {
  222. var refreshTokenExpire = _sysConfigService.GetRefreshTokenExpire().GetAwaiter().GetResult();
  223. return JWTEncryption.GenerateRefreshToken(accessToken, refreshTokenExpire);
  224. }
  225. /// <summary>
  226. /// 退出系统
  227. /// </summary>
  228. [DisplayName("退出系统")]
  229. public void Logout()
  230. {
  231. if (string.IsNullOrWhiteSpace(_userManager.Account))
  232. throw Oops.Oh(ErrorCodeEnum.D1011);
  233. _httpContextAccessor.HttpContext.SignoutToSwagger();
  234. }
  235. /// <summary>
  236. /// 获取登录配置
  237. /// </summary>
  238. /// <returns></returns>
  239. [AllowAnonymous]
  240. [SuppressMonitor]
  241. [DisplayName("获取登录配置")]
  242. public async Task<dynamic> GetLoginConfig()
  243. {
  244. var secondVerEnabled = await _sysConfigService.GetConfigValue<bool>(CommonConst.SysSecondVer);
  245. var captchaEnabled = await _sysConfigService.GetConfigValue<bool>(CommonConst.SysCaptcha);
  246. return new { SecondVerEnabled = secondVerEnabled, CaptchaEnabled = captchaEnabled };
  247. }
  248. /// <summary>
  249. /// 获取水印配置
  250. /// </summary>
  251. /// <returns></returns>
  252. [SuppressMonitor]
  253. [DisplayName("获取水印配置")]
  254. public async Task<dynamic> GetWatermarkConfig()
  255. {
  256. var watermarkEnabled = await _sysConfigService.GetConfigValue<bool>(CommonConst.SysWatermark);
  257. return new { WatermarkEnabled = watermarkEnabled };
  258. }
  259. /// <summary>
  260. /// 获取验证码
  261. /// </summary>
  262. /// <returns></returns>
  263. [AllowAnonymous]
  264. [SuppressMonitor]
  265. [DisplayName("获取验证码")]
  266. public dynamic GetCaptcha()
  267. {
  268. var codeId = YitIdHelper.NextId().ToString();
  269. var captcha = _captcha.Generate(codeId);
  270. return new { Id = codeId, Img = captcha.Base64 };
  271. }
  272. /// <summary>
  273. /// Swagger登录检查
  274. /// </summary>
  275. /// <returns></returns>
  276. [AllowAnonymous]
  277. [HttpPost("/api/swagger/checkUrl"), NonUnify]
  278. [DisplayName("Swagger登录检查")]
  279. public int SwaggerCheckUrl()
  280. {
  281. return _httpContextAccessor.HttpContext.User.Identity.IsAuthenticated ? 200 : 401;
  282. }
  283. /// <summary>
  284. /// Swagger登录提交
  285. /// </summary>
  286. /// <param name="auth"></param>
  287. /// <returns></returns>
  288. [AllowAnonymous]
  289. [HttpPost("/api/swagger/submitUrl"), NonUnify]
  290. [DisplayName("Swagger登录提交")]
  291. public async Task<int> SwaggerSubmitUrl([FromForm] SpecificationAuth auth)
  292. {
  293. try
  294. {
  295. _sysCacheService.Set(CommonConst.SysCaptcha, false);
  296. await Login(new LoginInput
  297. {
  298. Account = auth.UserName,
  299. Password = CryptogramUtil.SM2Encrypt(auth.Password),
  300. });
  301. _sysCacheService.Remove(CommonConst.SysCaptcha);
  302. return 200;
  303. }
  304. catch (Exception)
  305. {
  306. return 401;
  307. }
  308. }
  309. }