using Furion.SpecificationDocument; using Lazy.Captcha.Core; using Microsoft.Extensions.Caching.Memory; namespace Admin.NET.Core.Service; /// /// 系统登录授权服务 /// [ApiDescriptionSettings(Order = 200)] public class SysAuthService : IDynamicApiController, ITransient { private readonly UserManager _userManager; private readonly SqlSugarRepository _sysUserRep; private readonly RefreshTokenOptions _refreshTokenOptions; private readonly IHttpContextAccessor _httpContextAccessor; private readonly IEventPublisher _eventPublisher; private readonly SysUserService _sysUserService; private readonly SysMenuService _sysMenuService; private readonly SysOnlineUserService _sysOnlineUserService; private readonly IMemoryCache _cache; private readonly ICaptcha _captcha; public SysAuthService(UserManager userManager, SqlSugarRepository sysUserRep, IOptions refreshTokenOptions, IHttpContextAccessor httpContextAccessor, IEventPublisher eventPublisher, SysUserService sysUserService, SysMenuService sysMenuService, SysOnlineUserService sysOnlineUserService, IMemoryCache cache, ICaptcha captcha) { _userManager = userManager; _sysUserRep = sysUserRep; _httpContextAccessor = httpContextAccessor; _refreshTokenOptions = refreshTokenOptions.Value; _eventPublisher = eventPublisher; _sysUserService = sysUserService; _sysMenuService = sysMenuService; _sysOnlineUserService = sysOnlineUserService; _cache = cache; _captcha = captcha; } /// /// 登录系统 /// /// /// 用户名/密码:superadmin/123456 /// [HttpPost("/login")] [AllowAnonymous] [SuppressMonitor] public async Task Login([Required] LoginInput input) { // 判断验证码 if (!_captcha.Validate(input.CodeId.ToString(), input.Code)) throw Oops.Oh(ErrorCodeEnum.D0009); var encryptPasswod = MD5Encryption.Encrypt(input.Password); // 判断用户名密码 var user = await _sysUserRep.GetFirstAsync(u => u.Account.Equals(input.Account) && u.Password.Equals(encryptPasswod)); _ = user ?? throw Oops.Oh(ErrorCodeEnum.D1000); // 账号是否被冻结 if (user.Status == StatusEnum.Disable) throw Oops.Oh(ErrorCodeEnum.D1017); // 单用户登录 await _sysOnlineUserService.SignleLogin(user.Id); // 生成Token令牌 var accessToken = JWTEncryption.Encrypt(new Dictionary { {ClaimConst.UserId, user.Id}, {ClaimConst.TenantId, user.TenantId}, {ClaimConst.Account, user.Account}, {ClaimConst.RealName, user.RealName}, {ClaimConst.AccountType, user.AccountType }, {ClaimConst.OrgId, user.OrgId}, }); // 生成刷新Token令牌 var refreshToken = JWTEncryption.GenerateRefreshToken(accessToken, _refreshTokenOptions.ExpiredTime); // 设置响应报文头 _httpContextAccessor.HttpContext.SetTokensOfResponseHeaders(accessToken, refreshToken); // Swagger Knife4UI-AfterScript登录脚本 // ke.global.setAllHeader('Authorization', 'Bearer ' + ke.response.headers['access-token']); return new LoginOutput { AccessToken = accessToken, RefreshToken = refreshToken }; } /// /// 获取用户信息 /// /// [HttpGet("/userInfo")] public async Task GetUserInfo() { var user = _userManager.User; if (user == null) throw Oops.Oh(ErrorCodeEnum.D1011); var org = await _sysUserRep.ChangeRepository>().GetFirstAsync(u => u.Id == user.OrgId); var pos = await _sysUserRep.ChangeRepository>().GetFirstAsync(u => u.Id == user.PosId); // 按钮权限集合 var buttons = await _sysMenuService.GetPermCodeList(); // 登录日志 var ip = _httpContextAccessor.HttpContext.GetRemoteIpAddressToIPv4(); var client = Parser.GetDefault().Parse(_httpContextAccessor.HttpContext.Request.Headers["User-Agent"]); //var ipInfo = IpTool.Search(ip); //var address = ipInfo.Country + ipInfo.Province + ipInfo.City + "[" + ipInfo.NetworkOperator + "][" + ipInfo.Latitude + ipInfo.Longitude + "]"; await _eventPublisher.PublishAsync("Add:VisLog", new SysLogVis { Success = YesNoEnum.Y, Message = "登录", Ip = ip, //Location = address, Browser = client.UA.Family + client.UA.Major, Os = client.OS.Family + client.OS.Major, VisType = LoginTypeEnum.Login, Account = user.Account, RealName = user.RealName }); return new LoginUserOutput { Account = user.Account, RealName = user.RealName, Avatar = user.Avatar, Address = user.Address, Signature = user.Signature, OrgId = user.OrgId, OrgName = org?.Name, PosName = pos?.Name, Buttons = buttons }; } /// /// 获取刷新Token /// /// /// [HttpPost("/getRefreshToken")] public string RefreshToken([Required] string accessToken) { return JWTEncryption.GenerateRefreshToken(accessToken, _refreshTokenOptions.ExpiredTime); } /// /// 退出系统 /// [HttpPost("/logout")] public async void Logout() { var user = _userManager.User; if (user == null) throw Oops.Oh(ErrorCodeEnum.D1011); // 设置响应报文头 _httpContextAccessor.HttpContext.SetTokensOfResponseHeaders(null, null); // 退出日志 await _eventPublisher.PublishAsync("Add:VisLog", new SysLogVis { Success = YesNoEnum.Y, Message = "退出", VisType = LoginTypeEnum.Logout, Ip = _httpContextAccessor.HttpContext.GetRemoteIpAddressToIPv4(), Account = user.Account, RealName = user.RealName }); } /// /// 生成图片验证码 /// /// [HttpGet("/captcha")] [AllowAnonymous] [SuppressMonitor] public dynamic GetCaptcha() { var codeId = Yitter.IdGenerator.YitIdHelper.NextId(); var captcha = _captcha.Generate(codeId.ToString()); return new { Id = codeId, Img = captcha.Base64 }; } /// /// Swagger登录检查 /// /// [HttpPost("/Swagger/CheckUrl"), NonUnify] [AllowAnonymous] public int SwaggerCheckUrl() { return _cache.Get(CacheConst.SwaggerLogin) ? 200 : 401; } /// /// Swagger登录 /// /// /// [HttpPost("/Swagger/SubmitUrl"), NonUnify] [AllowAnonymous] public int SwaggerSubmitUrl([FromForm] SpecificationAuth auth) { var userName = App.GetConfig("SpecificationDocumentSettings:LoginInfo:UserName"); var password = App.GetConfig("SpecificationDocumentSettings:LoginInfo:Password"); if (auth.UserName == userName && auth.Password == password) { _cache.Set(CacheConst.SwaggerLogin, true); return 200; } return 401; } }