AuthService.cs 11 KB

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