IdentityAppService.cs 3.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. using System.Security.Claims;
  2. using Hotline.Identity.Accounts;
  3. using Hotline.Share.Dtos.Identity;
  4. using Hotline.Share.Enums.Identity;
  5. using Hotline.Users;
  6. using IdentityModel;
  7. using Microsoft.AspNetCore.Identity;
  8. using Microsoft.Extensions.Options;
  9. using XF.Domain.Authentications;
  10. using XF.Domain.Dependency;
  11. using XF.Domain.Exceptions;
  12. using XF.Domain.Options;
  13. using XF.Utility.AppIdentityModel;
  14. namespace Hotline.Application.Identity;
  15. public class IdentityAppService : IIdentityAppService, IScopeDependency
  16. {
  17. private readonly IAccountRepository _accountRepository;
  18. private readonly IAccountDomainService _accountDomainService;
  19. private readonly IUserRepository _userRepository;
  20. private readonly IJwtSecurity _jwtSecurity;
  21. private readonly IOptionsSnapshot<IdentityConfiguration> _identityOptionsAccessor;
  22. public IdentityAppService(
  23. IAccountRepository accountRepository,
  24. IAccountDomainService accountDomainService,
  25. IUserRepository userRepository,
  26. IJwtSecurity jwtSecurity,
  27. IOptionsSnapshot<IdentityConfiguration> identityOptionsAccessor)
  28. {
  29. _accountRepository = accountRepository;
  30. _accountDomainService = accountDomainService;
  31. _userRepository = userRepository;
  32. _jwtSecurity = jwtSecurity;
  33. _identityOptionsAccessor = identityOptionsAccessor;
  34. }
  35. public async Task<string> LoginAsync(LoginDto dto, CancellationToken cancellationToken)
  36. {
  37. var account = await _accountRepository.GetExtAsync(
  38. d => d.UserName == dto.UserName,
  39. d => d.Includes(x => x.Roles));
  40. if (account == null)
  41. throw UserFriendlyException.SameMessage("用户名或密码错误!");
  42. if (account.Status != EAccountStatus.Normal)
  43. throw UserFriendlyException.SameMessage("用户名或密码错误!");
  44. if (account.LockoutEnabled && account.LockoutEnd >= DateTime.Now)
  45. throw UserFriendlyException.SameMessage("账号被锁定!");
  46. var verifyResult = _accountDomainService.VerifyPassword(account, dto.Password);
  47. if (verifyResult == PasswordVerificationResult.Failed)
  48. {
  49. var lockoutOptions = _identityOptionsAccessor.Value.Lockout;
  50. account.AccessFailedCount += 1;
  51. if (account.LockoutEnabled && account.AccessFailedCount >= lockoutOptions.MaxFailedAccessAttempts)
  52. account.LockoutEnd = DateTime.Now.Add(lockoutOptions.DefaultLockoutTimeSpan);
  53. await _accountRepository.UpdateAsync(account, cancellationToken);
  54. throw UserFriendlyException.SameMessage("账号名或密码错误!");
  55. }
  56. account.LockoutEnd = null;
  57. account.AccessFailedCount = 0;
  58. await _accountRepository.UpdateAsync(account, cancellationToken);
  59. var user = await _userRepository.Queryable()
  60. .Includes(d => d.Organization)
  61. .FirstAsync(d => d.Id == account.Id);
  62. if (user == null)
  63. throw UserFriendlyException.SameMessage("未查询到用户数据");
  64. var jwtOptions = _identityOptionsAccessor.Value.Jwt;
  65. var claims = new List<Claim>
  66. {
  67. //new(JwtClaimTypes.Id, account.Id),
  68. new(JwtClaimTypes.Subject, account.Id),
  69. new(JwtClaimTypes.PhoneNumber, account.PhoneNo ?? string.Empty),
  70. new(AppClaimTypes.UserDisplayName, account.Name),
  71. new(JwtClaimTypes.Scope,jwtOptions.Scope),
  72. new(AppClaimTypes.UserPasswordChanged, account.PasswordChanged.ToString()),
  73. new(AppClaimTypes.DepartmentId, user.OrgId??string.Empty),
  74. new(AppClaimTypes.DepartmentCode, user.OrgCode??string.Empty),
  75. new(AppClaimTypes.DepartmentName, user.Organization?.OrgName??string.Empty),
  76. };
  77. claims.AddRange(account.Roles.Select(d => new Claim(JwtClaimTypes.Role, d.Name)));
  78. var token = _jwtSecurity.EncodeJwtToken(claims);
  79. return token;
  80. }
  81. }