#region copyright //------------------------------------------------------------------------------ // 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 // 此代码版权(除特别声明外的代码)归作者本人Diego所有 // 源代码使用协议遵循本仓库的开源协议及附加协议 // Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway // Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway // 使用文档:https://diego2098.gitee.io/thingsgateway-docs/ // QQ群:605534569 //------------------------------------------------------------------------------ #endregion using Furion; using Furion.DataEncryption; using Furion.EventBus; using Furion.FriendlyException; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Http; using System.Security.Claims; using ThingsGateway.Admin.Core; using Yitter.IdGenerator; namespace ThingsGateway.Admin.Application; /// public class AuthService : IAuthService { private readonly IConfigService _configService; private readonly IEventPublisher _eventPublisher; private readonly INoticeService _noticeService; private readonly ISysUserService _userService; private readonly IVerificatService _verificatService; /// public AuthService( IEventPublisher eventPublisher, ISysUserService userService, IConfigService configService, IVerificatService verificatService, INoticeService noticeService ) { _eventPublisher = eventPublisher; _userService = userService; _configService = configService; _verificatService = verificatService; _noticeService = noticeService; } /// public ValidCodeOutput GetCaptchaInfo() { //生成验证码 var captchInfo = new Random().Next(1111, 9999).ToString(); //生成请求号,并将验证码放入cache var reqNo = YitIdHelper.NextId(); //插入cache CacheStatic.Cache.Set(CacheConst.LOGIN_CAPTCHA + reqNo, captchInfo, TimeSpan.FromMinutes(1), false); //返回验证码和请求号 return new ValidCodeOutput { CodeValue = captchInfo, ValidCodeReqNo = reqNo }; } /// public Task GetLoginUserAsync() { return _userService.GetUserByIdAsync(UserManager.UserId); } /// public async Task LoginAsync(LoginInput input) { //判断是否有验证码 var sysBase = await _configService.GetByConfigKeyAsync(ConfigConst.SYS_CONFIGBASEDEFAULT, ConfigConst.CONFIG_CAPTCHA_OPEN); if (sysBase != null)//如果有这个配置项 { if (sysBase.ConfigValue.ToBoolean())//如果需要验证码 { //如果没填验证码,提示验证码不能为空 if (input.ValidCode.IsNullOrEmpty() || input.ValidCodeReqNo == 0) throw Oops.Bah("验证码不能为空").StatusCode(410); ValidValidCode(input.ValidCode, input.ValidCodeReqNo);//校验验证码 } } var password = DESCEncryption.Decrypt(input.Password, DESCKeyConst.DESCKey); // 解密 var userInfo = await _userService.GetUserByAccountAsync(input.Account) ?? throw Oops.Bah("用户不存在");//获取用户信息 if (userInfo.Password != password) throw Oops.Bah("账号密码错误");//账号密码错误 return await LoginAsync(userInfo, input.Device); } /// public async Task LogoutAsync() { //获取用户信息 var userinfo = await _userService.GetUserByAccountAsync(UserManager.UserAccount); if (userinfo != null) { LoginEvent loginEvent = new() { Ip = App.HttpContext.GetRemoteIpAddressToIPv4(), SysUser = userinfo, VerificatId = UserManager.VerificatId.ToLong(), }; await RemoveVerificatAsync(loginEvent);//移除验证Id } } /// /// 校验验证码方法 /// /// 验证码 /// 请求号 /// 是否从Cache删除 private static void ValidValidCode(string validCode, long validCodeReqNo, bool isDelete = true) { var code = CacheStatic.Cache.Get(CacheConst.LOGIN_CAPTCHA + validCodeReqNo, false);//从cache拿数据 if (isDelete) CacheStatic.Cache.Remove(CacheConst.LOGIN_CAPTCHA + validCodeReqNo);//删除验证码 if (code != null)//如果有 { //验证码如果不匹配直接抛错误,这里忽略大小写 if (validCode.ToLower() != code.ToLower()) throw Oops.Bah("验证码错误"); } else { throw Oops.Bah("验证码不能为空");//抛出验证码不能为空 } } /// /// 执行B端登录 /// /// 用户信息 /// 登录设备 /// private async Task LoginAsync(SysUser sysUser, AuthDeviceTypeEnum device) { if (sysUser.UserEnable == false) throw Oops.Bah("账号已停用");//账号已停用 var sysBase = await _configService.GetByConfigKeyAsync(ConfigConst.SYS_CONFIGBASEDEFAULT, ConfigConst.CONFIG_VERIFICAT_EXPIRES); var sessionid = YitIdHelper.NextId(); var identity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme); identity.AddClaim(new Claim(ClaimConst.VerificatId, sessionid.ToString())); identity.AddClaim(new Claim(ClaimConst.UserId, sysUser.Id.ToString())); identity.AddClaim(new Claim(ClaimConst.Account, sysUser.Account)); identity.AddClaim(new Claim(ClaimConst.IsSuperAdmin, sysUser.RoleCodeList.Contains(RoleConst.SuperAdmin).ToString())); identity.AddClaim(new Claim(ClaimConst.IsOpenApi, false.ToString())); var config = sysBase.ConfigValue.ToInt(2880); var diffTime = SysDateTimeExtensions.CurrentDateTime.AddMinutes(config); await App.HttpContext.SignInAsync(new ClaimsPrincipal(identity), new AuthenticationProperties() { IsPersistent = true, ExpiresUtc = diffTime, }); //登录事件参数 var loginEvent = new LoginEvent { Ip = App.HttpContext.GetRemoteIpAddressToIPv4(), Device = device, Expire = config, SysUser = sysUser, VerificatId = sessionid, }; await SetVerificatAsync(loginEvent);//写入verificat await _eventPublisher.PublishAsync(EventSubscriberConst.Login, loginEvent); //发布登录事件总线 return new LoginOutput { VerificatId = sessionid, Account = sysUser.Account }; } private async Task RemoveVerificatAsync(LoginEvent loginEvent) { //获取verificat列表 List verificatInfos = await _verificatService.GetVerificatIdAsync(loginEvent.SysUser.Id); if (verificatInfos != null) { //获取当前用户的verificat var verificat = verificatInfos.Where(it => it.Id == loginEvent.VerificatId).FirstOrDefault(); if (verificat != null) verificatInfos.Remove(verificat); //更新verificat列表 await _verificatService.SetVerificatIdAsync(loginEvent.SysUser.Id, verificatInfos); } await App.HttpContext?.SignOutAsync(); App.HttpContext?.SignoutToSwagger(); } /// /// 写入验证信息到缓存 /// /// /// private async Task SetVerificatAsync(LoginEvent loginEvent) { //获取verificat列表 List verificatInfos = await _verificatService.GetVerificatIdAsync(loginEvent.SysUser.Id); var verificatTimeout = loginEvent.DateTime.AddMinutes(loginEvent.Expire); //生成verificat信息 var verificatInfo = new VerificatInfo { Device = loginEvent.Device.ToString(), Expire = loginEvent.Expire, VerificatTimeout = verificatTimeout, Id = loginEvent.VerificatId, UserId = loginEvent.SysUser.Id, }; if (verificatInfos != null) { bool isSingle = false;//默认不开启单用户登录 var singleConfig = await _configService.GetByConfigKeyAsync(ConfigConst.SYS_CONFIGBASEDEFAULT, ConfigConst.CONFIG_SINGLE_OPEN);//获取系统单用户登录选项 if (singleConfig != null) isSingle = singleConfig.ConfigValue.ToBoolean();//如果配置不为空则设置单用户登录选项为系统配置的值 if (isSingle)//判断是否单用户登录 { await _noticeService.LogoutAsync(loginEvent.SysUser.Id, verificatInfos.Where(it => it.Device == loginEvent.Device.ToString()).ToList(), "该账号已在别处登录!");//通知其他用户下线 verificatInfos = verificatInfos.Where(it => it.Device != loginEvent.Device.ToString()).ToList();//去掉当前登录类型 verificatInfos.Add(verificatInfo);//添加到列表 } else { verificatInfos.Add(verificatInfo); } } else { verificatInfos = new List { verificatInfo };//直接就一个 } //添加到verificat列表 await _verificatService.SetVerificatIdAsync(loginEvent.SysUser.Id, verificatInfos); } }