using Admin.NET.Core.Service; using Admin.NET.Core; using Furion.DataEncryption; using Furion.DynamicApiController; using Furion.EventBus; using Microsoft.AspNetCore.Mvc; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Lazy.Captcha.Core; using Microsoft.AspNetCore.Http; using FZCZTB.NET.MD.CutomerMd; using Microsoft.AspNetCore.Authorization; using System.ComponentModel.DataAnnotations; using System.ComponentModel; using FZCZTB.NET.SYSService.MSM; using FZCTB.NET.API.Application.Auth.DTO; using Furion.FriendlyException; using FZCZTB.NET.SYSService.CustomerSYS; using Furion; using NewLife; using cylsg.utility.Extend; using static QRCoder.PayloadGenerator; using FZCZTB.NET.MD.CutomerMd.Extend; using Furion.DependencyInjection; using Microsoft.Extensions.Options; namespace FZCTB.NET.API.Application.Auth { /// /// 鉴权服务 /// [ApiDescriptionSettings("FZCAPISYS", Order = 149)] public class AuthService: IDynamicApiController { private readonly SMSConfigOptions _smsOptions; private readonly SqlSugarRepository _sysUserRep; private readonly SqlSugarRepository _sysUserExRol; private readonly SqlSugarRepository _sysExRol; private readonly IHttpContextAccessor _httpContextAccessor; private readonly SysMenuService _sysMenuService; private readonly SysOnlineUserService _sysOnlineUserService; private readonly SysConfigService _sysConfigService; private readonly SysUserService _sysUserService; private readonly ZCSMSService _sysSmsService; private readonly SysLdapService _sysLdapService; private readonly ICaptcha _captcha; private readonly IEventPublisher _eventPublisher; private readonly SysCacheService _sysCacheService; public AuthService( SqlSugarRepository sysUserRep, SqlSugarRepository sysUserExRol, IHttpContextAccessor httpContextAccessor, SysOnlineUserService sysOnlineUserService, SysConfigService sysConfigService, SysLdapService sysLdapService, IEventPublisher eventPublisher, ZCSMSService sysSmsService, SysCacheService sysCacheService, SysMenuService sysMenuService, SysUserService sysUserService, IOptions smsConfigOptions, SqlSugarRepository exrel, ICaptcha captcha ) { _captcha = captcha; _sysUserRep = sysUserRep; _sysSmsService = sysSmsService; _eventPublisher = eventPublisher; _sysUserService = sysUserService; _sysMenuService = sysMenuService; _sysCacheService = sysCacheService; _sysConfigService = sysConfigService; _httpContextAccessor = httpContextAccessor; _sysOnlineUserService = sysOnlineUserService; _sysLdapService = sysLdapService; _smsOptions = smsConfigOptions.Value; _sysExRol = exrel; _sysUserExRol = sysUserExRol; } /// /// 手机号登录 🔖 /// /// /// [AllowAnonymous] [DisplayName("手机号登录")] public virtual async Task LoginPhone([Required] CustomerLoginPhoneInput input) { if(input.Id>0) { //最后一次确认登陆状态 var verifyCode = _sysCacheService.Get($"{CacheConst.KeyPhoneVerCode}{input.Phone}"); if (string.IsNullOrWhiteSpace(verifyCode)) throw Oops.Oh("验证码不存在或已失效,请重新获取!"); _sysCacheService.Remove($"{CacheConst.KeyPhoneVerCode}{input.Phone}"); if (verifyCode != input.Code) throw Oops.Oh("登录码失效"); var user = await _sysUserRep.AsQueryable().Where(x => x.Id == input.Id && x.IsEn == true).FirstAsync(); if(user == null) throw Oops.Oh("没有找到该手机用户"); return await CreateToken(user, input.ExRuleCode ?? ""); } if(input.Code!="TEST") // 校验短信验证码 _sysSmsService.VerifyCode(new SmsVerifyCodeInput { Phone = input.Phone, Code = input.Code }); // 获取登录租户和用户 // 获取登录租户和用户 var userList = await _sysUserRep.AsQueryable().Where(x => x.PhoneNumber == input.Phone&&x.IsEn==true).Includes(x=>x.CusExtend).ToListAsync(); if (userList == null) { throw Oops.Oh("没有找到该手机用户"); } if(userList.Count>1) { // 生成随机验证码 var random = new Random(); var verifyCode = random.Next(100000, 999999); //需要二次登陆 var aRet = new CustomerLoginOutput { TheLastLogo = false, CustomerExs = new List(), Code = verifyCode.ToString(), }; _sysCacheService.Set($"{CacheConst.KeyPhoneVerCode}{input.Phone}", verifyCode.ToString(), TimeSpan.FromSeconds(_smsOptions.lingKai.VerifyTimeOut.ToInt())); foreach (var item in userList) { aRet.CustomerExs.Add(new CustomerExVm { CustomerUserID = item.Id, EnterpriseName = item.CusExtend.EnterpriseName, UnifiedSocialCreditCode = item.CusExtend.UnifiedSocialCreditCode.MaskMiddle(), IsManger = item.IsManager }); } return aRet; } return await CreateToken(userList[0], input.ExRuleCode??""); } /// /// 切换角色 已登录进去角色或者切换角色是调用,需要替换Token,相当于登录 /// [DisplayName("切换角色")] public async Task ChangeLogoInExRule(string RuleCode) { var id = App.User.FindFirst(ClaimConst.UserId)?.Value.ToLong() ?? 0; if (id == 0) { throw Oops.Oh("用户凭证错误"); } var rols = await _sysExRol.GetFirstAsync(x => x.Code == RuleCode && x.Status == StatusEnum.Enable); if(rols==null) throw Oops.Oh("角色已经下线"); // 获取登录租户和用户 // 获取登录租户和用户 var user = await _sysUserRep.AsQueryable().Where(x => x.Id == id).FirstAsync(); if (user == null) { throw Oops.Oh("该用户没有注册"); } if (user.IsEn == false) { throw Oops.Oh("用异常"); } var exr= await _sysUserExRol.GetFirstAsync(x=>x.CusExtendId==user.CusExtendId&&x.ExRoleId== rols.Id); if (exr==null) throw Oops.Oh("没有申请该角色"); return await CreateToken(user, RuleCode); } /// /// 手机号登录 🔖 /// /// /// [AllowAnonymous] [DisplayName("微信扫码登录")] public virtual async Task WeiXinLoginPhone([Required] CustomerLoginPhoneInput input) { throw Oops.Oh("暂时不支持微信扫码登录"); // 校验短信验证码 //_sysSmsService.VerifyCode(new SmsVerifyCodeInput { Phone = input.Phone, Code = input.Code }); //// 获取登录租户和用户 //var user = await _sysUserRep.AsQueryable().Where(x=>x.Account==input.Phone).Includes(x => x.CoutomerExRols, y => y.ExRole).FirstAsync(); //if(user==null) //{ //} //return await CreateToken(user, input.ExRuleCode ); } /// /// 生成Token令牌 🔖 /// /// \ /// \ /// [NonAction] internal async Task CreateToken(FBS_CustormerUsers user,string ExRuleCode, SysUserEventTypeEnum sysUserEventTypeEnum = SysUserEventTypeEnum.Login) { // 单用户登录 await _sysOnlineUserService.SingleLogin(user.Id); // 生成Token令牌 var tokenExpire = await _sysConfigService.GetTokenExpire(); var accessToken = JWTEncryption.Encrypt(new Dictionary { { ClaimConst.UserId, user.Id }, { ClaimConst.TenantId, user.CusExtendId }, { ClaimConst.Account, user.PhoneNumber.PrivacyStr() }, { ClaimConst.RealName, user.Nickname??user.Name }, { ClaimConst.UserType, "Customer" }, { ClaimConst.CustomerExId, user.CusExtendId }, { ClaimConst.CustomerLogoinType, ExRuleCode }, }, tokenExpire); // 生成刷新Token令牌 var refreshTokenExpire = await _sysConfigService.GetRefreshTokenExpire(); var refreshToken = JWTEncryption.GenerateRefreshToken(accessToken, refreshTokenExpire); // 设置响应报文头 _httpContextAccessor.HttpContext.SetTokensOfResponseHeaders(accessToken, refreshToken); // Swagger Knife4UI-AfterScript登录脚本 // ke.global.setAllHeader('Authorization', 'Bearer ' + ke.response.headers['access-token']); // 更新用户登录信息 user.LastLoginIp = _httpContextAccessor.HttpContext.GetRemoteIpAddressToIPv4(true); (user.LastLoginAddress, double? longitude, double? latitude) = CommonUtil.GetIpAddress(user.LastLoginIp); user.LastLoginTime = DateTime.Now; user.LastLoginDevice = CommonUtil.GetClientDeviceInfo(_httpContextAccessor.HttpContext?.Request?.Headers?.UserAgent); await _sysUserRep.AsUpdateable(user).UpdateColumns(u => new { u.LastLoginIp, u.LastLoginAddress, u.LastLoginTime, u.LastLoginDevice, }).ExecuteCommandAsync(); var payload = new { Entity = user, Output = new CustomerLoginOutput { AccessToken = accessToken, RefreshToken = refreshToken, TheLastLogo=true, } }; payload.Output.ExRoles = new List(); payload.Output.ExRoles = await _sysUserExRol.AsQueryable().Where(x => x.CusExtendId == user.CusExtendId).Includes(x => x.ExRole).Select(x => new CustomerExRoleVm { Code = x.ExRole.Code, HasFlsh = x.steps == CusExtendStep.Pass, Name = x.ExRole.Name }).ToListAsync(); //暂时不出用户事件 // 发布系统用户操作事件 //await _eventPublisher.PublishAsync(sysUserEventTypeEnum, payload); return payload.Output; } } }