1
0

AuthService.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. using System;
  2. using System.Linq;
  3. using System.Threading.Tasks;
  4. using ZhonTai.Admin.Core.Configs;
  5. using ZhonTai.Common.Helpers;
  6. using ZhonTai.Admin.Core.Dto;
  7. using ZhonTai.Admin.Domain.Permission;
  8. using ZhonTai.Admin.Domain.User;
  9. using ZhonTai.Admin.Domain.Tenant;
  10. using ZhonTai.Admin.Services.Auth.Dto;
  11. using ZhonTai.Admin.Domain.RolePermission;
  12. using ZhonTai.Admin.Domain.UserRole;
  13. using ZhonTai.Admin.Services.Contracts;
  14. using ZhonTai.Admin.Tools.Captcha;
  15. using ZhonTai.DynamicApi;
  16. using ZhonTai.DynamicApi.Attributes;
  17. using Microsoft.AspNetCore.Authorization;
  18. using Microsoft.AspNetCore.Cors;
  19. using StackExchange.Profiling;
  20. using Microsoft.AspNetCore.Mvc;
  21. using ZhonTai.Admin.Core.Attributes;
  22. using ZhonTai.Admin.Services.LoginLog.Dto;
  23. using System.Diagnostics;
  24. using ZhonTai.Admin.Services.LoginLog;
  25. using ZhonTai.Admin.Core.Auth;
  26. using System.Security.Claims;
  27. using Microsoft.AspNetCore.Mvc.ModelBinding;
  28. using ZhonTai.Common.Extensions;
  29. using ZhonTai.Admin.Services.User;
  30. using ZhonTai.Admin.Core.Consts;
  31. namespace ZhonTai.Admin.Services.Auth
  32. {
  33. /// <summary>
  34. /// 认证授权服务
  35. /// </summary>
  36. [DynamicApi(Area = "admin")]
  37. public class AuthService : BaseService, IAuthService, IDynamicApi
  38. {
  39. private readonly AppConfig _appConfig;
  40. private readonly IPermissionRepository _permissionRepository;
  41. private readonly IUserRepository _userRepository;
  42. private readonly ITenantRepository _tenantRepository;
  43. private readonly ICaptchaTool _captchaTool;
  44. public AuthService(
  45. AppConfig appConfig,
  46. IUserRepository userRepository,
  47. IPermissionRepository permissionRepository,
  48. ITenantRepository tenantRepository,
  49. ICaptchaTool captchaTool
  50. )
  51. {
  52. _appConfig = appConfig;
  53. _userRepository = userRepository;
  54. _permissionRepository = permissionRepository;
  55. _tenantRepository = tenantRepository;
  56. _captchaTool = captchaTool;
  57. }
  58. /// <summary>
  59. /// 获得token
  60. /// </summary>
  61. /// <param name="user">用户信息</param>
  62. /// <returns></returns>
  63. private string GetToken(AuthLoginOutput user)
  64. {
  65. if (user == null)
  66. {
  67. return string.Empty;
  68. }
  69. var token = LazyGetRequiredService<IUserToken>().Create(new[]
  70. {
  71. new Claim(ClaimAttributes.UserId, user.Id.ToString()),
  72. new Claim(ClaimAttributes.UserName, user.UserName),
  73. new Claim(ClaimAttributes.UserNickName, user.NickName),
  74. new Claim(ClaimAttributes.TenantId, user.TenantId.ToString()),
  75. new Claim(ClaimAttributes.TenantType, user.TenantType.ToString()),
  76. new Claim(ClaimAttributes.DataIsolationType, user.DataIsolationType.ToString())
  77. });
  78. return token;
  79. }
  80. /// <summary>
  81. /// 查询密钥
  82. /// </summary>
  83. /// <returns></returns>
  84. [HttpGet]
  85. [AllowAnonymous]
  86. [NoOprationLog]
  87. public async Task<IResultOutput> GetPasswordEncryptKeyAsync()
  88. {
  89. //写入Redis
  90. var guid = Guid.NewGuid().ToString("N");
  91. var key = string.Format(CacheKey.PassWordEncryptKey, guid);
  92. var encyptKey = StringHelper.GenerateRandom(8);
  93. await Cache.SetAsync(key, encyptKey, TimeSpan.FromMinutes(5));
  94. var data = new { key = guid, encyptKey };
  95. return ResultOutput.Ok(data);
  96. }
  97. /// <summary>
  98. /// 查询用户信息
  99. /// </summary>
  100. /// <returns></returns>
  101. [Login]
  102. public async Task<IResultOutput> GetUserInfoAsync()
  103. {
  104. if (!(User?.Id > 0))
  105. {
  106. return ResultOutput.NotOk("未登录!");
  107. }
  108. var authUserInfoOutput = new AuthUserInfoOutput { };
  109. //用户信息
  110. authUserInfoOutput.User = await _userRepository.GetAsync<AuthUserProfileDto>(User.Id);
  111. //用户菜单
  112. authUserInfoOutput.Menus = await _permissionRepository.Select
  113. .Where(a => new[] { PermissionTypeEnum.Group, PermissionTypeEnum.Menu }.Contains(a.Type))
  114. .Where(a =>
  115. _permissionRepository.Orm.Select<RolePermissionEntity>()
  116. .InnerJoin<UserRoleEntity>((b, c) => b.RoleId == c.RoleId && c.UserId == User.Id)
  117. .Where(b => b.PermissionId == a.Id)
  118. .Any()
  119. )
  120. .OrderBy(a => a.ParentId)
  121. .OrderBy(a => a.Sort)
  122. .ToListAsync(a => new AuthUserMenuDto { ViewPath = a.View.Path });
  123. //用户权限点
  124. authUserInfoOutput.Permissions = await _permissionRepository.Select
  125. .Where(a => a.Type == PermissionTypeEnum.Dot)
  126. .Where(a =>
  127. _permissionRepository.Orm.Select<RolePermissionEntity>()
  128. .InnerJoin<UserRoleEntity>((b, c) => b.RoleId == c.RoleId && c.UserId == User.Id)
  129. .Where(b => b.PermissionId == a.Id)
  130. .Any()
  131. )
  132. .ToListAsync(a => a.Code);
  133. return ResultOutput.Ok(authUserInfoOutput);
  134. }
  135. /// <summary>
  136. /// 登录
  137. /// </summary>
  138. /// <param name="input"></param>
  139. /// <returns></returns>
  140. [HttpPost]
  141. [AllowAnonymous]
  142. [NoOprationLog]
  143. public async Task<IResultOutput> LoginAsync(AuthLoginInput input)
  144. {
  145. var sw = new Stopwatch();
  146. sw.Start();
  147. #region 验证码校验
  148. if (_appConfig.VarifyCode.Enable)
  149. {
  150. input.Captcha.DeleteCache = true;
  151. input.Captcha.CaptchaKey = CacheKey.CaptchaKey;
  152. var isOk = await _captchaTool.CheckAsync(input.Captcha);
  153. if (!isOk)
  154. {
  155. return ResultOutput.NotOk("安全验证不通过,请重新登录!");
  156. }
  157. }
  158. #endregion 验证码校验
  159. UserEntity user = null;
  160. user = await _userRepository.Select.DisableGlobalFilter("Tenant").Where(a => a.UserName == input.UserName).ToOneAsync();
  161. if (!(user?.Id > 0))
  162. {
  163. return ResultOutput.NotOk("账号输入有误!", 3);
  164. }
  165. #region 解密
  166. if (input.PasswordKey.NotNull())
  167. {
  168. var passwordEncryptKey = string.Format(CacheKey.PassWordEncryptKey, input.PasswordKey);
  169. var existsPasswordKey = await Cache.ExistsAsync(passwordEncryptKey);
  170. if (existsPasswordKey)
  171. {
  172. var secretKey = await Cache.GetAsync(passwordEncryptKey);
  173. if (secretKey.IsNull())
  174. {
  175. return ResultOutput.NotOk("解密失败!", 1);
  176. }
  177. input.Password = DesEncrypt.Decrypt(input.Password, secretKey);
  178. await Cache.DelAsync(passwordEncryptKey);
  179. }
  180. else
  181. {
  182. return ResultOutput.NotOk("解密失败!", 1);
  183. }
  184. }
  185. #endregion 解密
  186. var password = MD5Encrypt.Encrypt32(input.Password);
  187. if (user.Password != password)
  188. {
  189. return ResultOutput.NotOk("密码输入有误!", 4);
  190. }
  191. var authLoginOutput = Mapper.Map<AuthLoginOutput>(user);
  192. if (_appConfig.Tenant)
  193. {
  194. var tenant = await _tenantRepository.Select.DisableGlobalFilter("Tenant").WhereDynamic(user.TenantId).ToOneAsync(a => new { a.TenantType, a.DataIsolationType });
  195. authLoginOutput.TenantType = tenant.TenantType;
  196. authLoginOutput.DataIsolationType = tenant.DataIsolationType;
  197. }
  198. string token = GetToken(authLoginOutput);
  199. sw.Stop();
  200. #region 添加登录日志
  201. var loginLogAddInput = new LoginLogAddInput()
  202. {
  203. CreatedUserName = input.UserName,
  204. ElapsedMilliseconds = sw.ElapsedMilliseconds,
  205. Status = true
  206. };
  207. loginLogAddInput.CreatedUserId = authLoginOutput.Id;
  208. loginLogAddInput.NickName = authLoginOutput.NickName;
  209. loginLogAddInput.TenantId = authLoginOutput.TenantId;
  210. await LazyGetRequiredService<ILoginLogService>().AddAsync(loginLogAddInput);
  211. #endregion 添加登录日志
  212. return ResultOutput.Ok(new { token = token });
  213. }
  214. /// <summary>
  215. /// 刷新Token
  216. /// 以旧换新
  217. /// </summary>
  218. /// <param name="token"></param>
  219. /// <returns></returns>
  220. [HttpGet]
  221. [AllowAnonymous]
  222. public async Task<IResultOutput> Refresh([BindRequired] string token)
  223. {
  224. var userClaims = LazyGetRequiredService<IUserToken>().Decode(token);
  225. if (userClaims == null || userClaims.Length == 0)
  226. {
  227. return ResultOutput.NotOk();
  228. }
  229. var refreshExpires = userClaims.FirstOrDefault(a => a.Type == ClaimAttributes.RefreshExpires)?.Value;
  230. if (refreshExpires.IsNull())
  231. {
  232. return ResultOutput.NotOk();
  233. }
  234. if (refreshExpires.ToLong() <= DateTime.Now.ToTimestamp())
  235. {
  236. return ResultOutput.NotOk("登录信息已过期");
  237. }
  238. var userId = userClaims.FirstOrDefault(a => a.Type == ClaimAttributes.UserId)?.Value;
  239. if (userId.IsNull())
  240. {
  241. return ResultOutput.NotOk("登录信息已失效");
  242. }
  243. var output = await LazyGetRequiredService<IUserService>().GetLoginUserAsync(userId.ToLong());
  244. string newToken = GetToken(output?.Data);
  245. return ResultOutput.Ok(new { token = newToken });
  246. }
  247. /// <summary>
  248. /// 获取验证数据
  249. /// </summary>
  250. /// <returns></returns>
  251. [HttpGet]
  252. [AllowAnonymous]
  253. [NoOprationLog]
  254. [EnableCors(AdminConsts.AllowAnyPolicyName)]
  255. public async Task<IResultOutput> GetCaptcha()
  256. {
  257. using (MiniProfiler.Current.Step("获取滑块验证"))
  258. {
  259. var data = await _captchaTool.GetAsync(CacheKey.CaptchaKey);
  260. return ResultOutput.Ok(data);
  261. }
  262. }
  263. /// <summary>
  264. /// 检查验证数据
  265. /// </summary>
  266. /// <returns></returns>
  267. [HttpGet]
  268. [AllowAnonymous]
  269. [NoOprationLog]
  270. [EnableCors(AdminConsts.AllowAnyPolicyName)]
  271. public async Task<IResultOutput> CheckCaptcha([FromQuery] CaptchaInput input)
  272. {
  273. input.CaptchaKey = CacheKey.CaptchaKey;
  274. var result = await _captchaTool.CheckAsync(input);
  275. return ResultOutput.Result(result);
  276. }
  277. }
  278. }