using AutoMapper; using IdentityService.Domain; using IdentityService.WebApi.Applications.Dtos; using IdentityService.WebApi.Applications.Dtos.Common; using IM.Commons; using IM.Jwt; using IM.InitCommon.Management; using Microsoft.Extensions.Options; using System.Security.Claims; namespace IdentityService.WebApi.Applications.Auth { public class AuthService { private readonly ITokenService tokenService; private readonly IIdRepository idRepository; private readonly IOptions jwtOptions; private readonly IdDomainService idDomainService; private readonly IMapper mapper; private readonly RuntimePolicy runtime; public AuthService(ITokenService tokenService, IIdRepository idRepository, IOptions options, IdDomainService idDomainService, IMapper mapper, RuntimePolicy runtime ) { this.tokenService = tokenService; this.idRepository = idRepository; jwtOptions = options; this.idDomainService = idDomainService; this.mapper = mapper; this.runtime = runtime; } public async Task> LoginAsync(string username, string password) { var user = await idRepository.FindByUserNameAsync(username); if (user is null) { return Result.Fail(ResultCode.USER_NOT_FOUND); } if (user.Status != UserState.Normal || user.IsDeleted) return Result.Fail(ResultCode.AUTH_FAILED); var idResult = await idRepository.CheckForSignInAsync(user, password, true); if (!idResult.Succeeded) { return Result.Fail(ResultCode.PASSWORD_ERROR); } var token = await BuildTokenAsync(user); var refreshToken = await tokenService.CreateRefreshTokenAsync(user.Id, stamp: user.SecurityStamp, days: runtime.Current.ClientRefreshDays); return Result.Success(new LoginResponse(user.Id, token, refreshToken, null, user.UserName, user.NickName,user.Avatar, user.CreationTime)); } public async Task> RegisterAsync(string userName, string password, string nickName) { if (!runtime.Current.RegistrationEnabled) return Result.Fail(ResultCode.PERMISSION_DENIED, "平台暂未开放注册"); if (password.Length < runtime.Current.PasswordMinLength) return Result.Fail(ResultCode.PARAMETER_ERROR, "密码不符合当前最小长度要求"); var userResult = await idDomainService.CreateUserAsync(userName, password, nickName); if (!userResult.Succeeded) return Result.Fail(userResult); var idResult = await idRepository.CreateAsync(userResult.Data!, password); if (!idResult.Succeeded) { var msg = idResult.Errors.FirstOrDefault(); return Result.Fail( ResultCode.REGISTER_ERROR, msg?.Description ?? ResultCode.REGISTER_ERROR.GetDescription()); } return Result.Success(mapper.Map(userResult.Data)); } public async Task> RefreshAsync(string refreshToken) { var validateRes = await tokenService.ValidateRefreshTokenAsync(refreshToken); if (!validateRes.ok) { return Result.Fail(ResultCode.AUTH_FAILED); } var user = await idRepository.FindByIdAsync(validateRes.userId); if (user is null) { return Result.Fail(ResultCode.USER_NOT_FOUND); } if (user.Status != UserState.Normal || user.IsDeleted || validateRes.stamp != user.SecurityStamp) return Result.Fail(ResultCode.AUTH_FAILED); var token = await BuildTokenAsync(user); await tokenService.RevokeRefreshTokenAsync(refreshToken); var nextRefresh = await tokenService.CreateRefreshTokenAsync(user.Id, stamp: user.SecurityStamp, days: runtime.Current.ClientRefreshDays); return Result.Success(new LoginResponse(user.Id, token, nextRefresh, null, user.UserName, user.NickName, user.Avatar, user.CreationTime)); } private async Task BuildTokenAsync(Domain.Entities.User user) { var roles = await idRepository.GetRolesAsync(user); List claims = new List(); claims.Add(new Claim(ClaimTypes.NameIdentifier, user.Id.ToString())); claims.Add(new Claim("session_stamp", user.SecurityStamp ?? "")); foreach (string role in roles) { claims.Add(new Claim(ClaimTypes.Role, role)); } var original = jwtOptions.Value; return tokenService.GetToken(claims, new JwtOptions { Key = original.Key, Issuer = original.Issuer, Audience = original.Audience, RefreshTokenDays = original.RefreshTokenDays, AccessTokenMinutes = runtime.Current.ClientAccessMinutes > 0 ? runtime.Current.ClientAccessMinutes : original.AccessTokenMinutes }); } } }