diff --git a/Infrastructure/Helper/RandomHelper.cs b/Infrastructure/Helper/RandomHelper.cs new file mode 100644 index 0000000..ca8e859 --- /dev/null +++ b/Infrastructure/Helper/RandomHelper.cs @@ -0,0 +1,25 @@ +using System; +using System.Text; + +namespace ZR.Infrastructure.Helper +{ + public class RandomHelper + { + /// + /// 生成n为验证码 + /// + /// + /// + public static string GenerateNum(int Length) + { + char[] constant = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' }; + StringBuilder newRandom = new(constant.Length); + Random rd = new(); + for (int i = 0; i < Length; i++) + { + newRandom.Append(constant[rd.Next(constant.Length - 1)]); + } + return newRandom.ToString(); + } + } +} diff --git a/ZR.Admin.WebApi/Controllers/System/SysLoginController.cs b/ZR.Admin.WebApi/Controllers/System/SysLoginController.cs index 217881c..d4c7599 100644 --- a/ZR.Admin.WebApi/Controllers/System/SysLoginController.cs +++ b/ZR.Admin.WebApi/Controllers/System/SysLoginController.cs @@ -1,10 +1,13 @@ using Lazy.Captcha.Core; using Microsoft.AspNetCore.Mvc; using ZR.Admin.WebApi.Filters; +using ZR.Infrastructure.Helper; using ZR.Model.System; using ZR.Model.System.Dto; using ZR.Service.System; using ZR.Service.System.IService; +using ZR.ServiceCore.Model.Dto; +using ZR.ServiceCore.Services; namespace ZR.Admin.WebApi.Controllers.System { @@ -21,6 +24,7 @@ namespace ZR.Admin.WebApi.Controllers.System private readonly ICaptcha SecurityCodeHelper; private readonly ISysConfigService sysConfigService; private readonly ISysRoleService roleService; + private readonly ISmsCodeLogService smsCodeLogService; public SysLoginController( ISysMenuService sysMenuService, @@ -29,6 +33,7 @@ namespace ZR.Admin.WebApi.Controllers.System ISysPermissionService permissionService, ISysConfigService configService, ISysRoleService sysRoleService, + ISmsCodeLogService smsCodeLogService, ICaptcha captcha) { SecurityCodeHelper = captcha; @@ -37,6 +42,7 @@ namespace ZR.Admin.WebApi.Controllers.System this.sysLoginService = sysLoginService; this.permissionService = permissionService; this.sysConfigService = configService; + this.smsCodeLogService = smsCodeLogService; roleService = sysRoleService; } @@ -236,7 +242,7 @@ namespace ZR.Admin.WebApi.Controllers.System { if (dto == null) { return ToResponse(ResultCode.CUSTOM_ERROR, "扫码失败"); } var name = App.HttpContext.GetName(); - + sysLoginService.CheckLockUser(name); TokenModel tokenModel = JwtUtil.GetLoginUser(HttpContext); @@ -246,12 +252,80 @@ namespace ZR.Admin.WebApi.Controllers.System dict.Add("status", "success"); dict.Add("token", JwtUtil.GenerateJwtToken(JwtUtil.AddClaims(tokenModel))); CacheService.SetScanLogin(dto.Uuid, dict); - + return SUCCESS(1); } return ToResponse(ResultCode.FAIL, "二维码已失效"); } #endregion + /// + /// + /// + /// + /// + [HttpPost("checkMobile")] + [Log(Title = "发送短息", BusinessType = BusinessType.INSERT)] + public IActionResult CheckMobile([FromBody] PhoneLoginDto dto) + { + dto.LoginIP = HttpContextExtension.GetClientUserIp(HttpContext); + + SysConfig sysConfig = sysConfigService.GetSysConfigByKey("sys.account.captchaOnOff"); + if (sysConfig?.ConfigValue != "off" && !SecurityCodeHelper.Validate(dto.Uuid, dto.Code, false)) + { + return ToResponse(ResultCode.CUSTOM_ERROR, "验证码错误"); + } + string location = HttpContextExtension.GetIpInfo(dto.LoginIP); + var info = sysUserService.GetFirst(f => f.Phonenumber == dto.PhoneNum) ?? throw new CustomException(ResultCode.CUSTOM_ERROR, "该手机号不存在", false); + + var smsCode = RandomHelper.GenerateNum(6); + var smsContent = $"验证码{smsCode}(随机验证码),有效期10分钟。"; + //TODO 发送短息验证码,1分钟内允许一次 + smsCodeLogService.AddSmscodeLog(new ServiceCore.Model.SmsCodeLog() + { + Userid = info.UserId, + PhoneNum = dto.PhoneNum.ParseToLong(), + AddTime = DateTime.Now, + SendType = 1, + SmsCode = smsCode, + SmsContent = smsContent, + UserIP = dto.LoginIP, + Location = location, + }); + CacheService.SetPhoneCode(dto.PhoneNum, smsCode); + + return SUCCESS(new { smsCode }); + } + + /// + /// 手机号登录 + /// + /// 登录对象 + /// + [Route("PhoneLogin")] + [HttpPost] + [Log(Title = "手机号登录")] + public IActionResult PhoneLogin([FromBody] PhoneLoginDto loginBody) + { + if (loginBody == null) { throw new CustomException("请求参数错误"); } + loginBody.LoginIP = HttpContextExtension.GetClientUserIp(HttpContext); + + if (!CacheService.CheckPhoneCode(loginBody.PhoneNum, loginBody.PhoneCode)) + { + return ToResponse(ResultCode.CUSTOM_ERROR, "短信验证码错误"); + } + var info = sysUserService.GetFirst(f => f.Phonenumber == loginBody.PhoneNum) ?? throw new CustomException(ResultCode.CUSTOM_ERROR, "该手机号不存在", false); + sysLoginService.CheckLockUser(info.UserName); + string location = HttpContextExtension.GetIpInfo(loginBody.LoginIP); + var user = sysLoginService.PhoneLogin(loginBody, new SysLogininfor() { LoginLocation = location }, info); + + List roles = roleService.SelectUserRoleListByUserId(user.UserId); + //权限集合 eg *:*:*,system:user:list + List permissions = permissionService.GetMenuPermission(user); + + TokenModel loginUser = new(user.Adapt(), roles.Adapt>()); + CacheService.SetUserPerms(GlobalConstant.UserPermKEY + user.UserId, permissions); + return SUCCESS(JwtUtil.GenerateJwtToken(JwtUtil.AddClaims(loginUser))); + } } } diff --git a/ZR.Admin.WebApi/Controllers/System/SysProfileController.cs b/ZR.Admin.WebApi/Controllers/System/SysProfileController.cs index ca2738a..61304a2 100644 --- a/ZR.Admin.WebApi/Controllers/System/SysProfileController.cs +++ b/ZR.Admin.WebApi/Controllers/System/SysProfileController.cs @@ -1,5 +1,6 @@ using Microsoft.AspNetCore.Mvc; using ZR.Admin.WebApi.Filters; +using ZR.Infrastructure.Helper; using ZR.Model.System; using ZR.Model.System.Dto; using ZR.Service.System.IService; diff --git a/ZR.Admin.WebApi/wwwroot/data.xlsx b/ZR.Admin.WebApi/wwwroot/data.xlsx index 6c84d58..8fca81c 100644 Binary files a/ZR.Admin.WebApi/wwwroot/data.xlsx and b/ZR.Admin.WebApi/wwwroot/data.xlsx differ diff --git a/ZR.ServiceCore/Model/Dto/PhoneLoginDto.cs b/ZR.ServiceCore/Model/Dto/PhoneLoginDto.cs new file mode 100644 index 0000000..37e11ad --- /dev/null +++ b/ZR.ServiceCore/Model/Dto/PhoneLoginDto.cs @@ -0,0 +1,25 @@ +using System.ComponentModel.DataAnnotations; + +namespace ZR.ServiceCore.Model.Dto +{ + public class PhoneLoginDto + { + /// + /// 验证码 + /// + public string Code { get; set; } + + /// + /// 唯一标识 + /// + public string Uuid { get; set; } = ""; + public string LoginIP { get; set; } + [Required(ErrorMessage = "手机号不能为空")] + public string PhoneNum { get; set; } + /// + /// 手机短信验证码 + /// + //[Required(ErrorMessage = "短信验证码不能为空")] + public string PhoneCode { get; set; } + } +} diff --git a/ZR.ServiceCore/Services/CacheService.cs b/ZR.ServiceCore/Services/CacheService.cs index 7ba1ead..7594b4f 100644 --- a/ZR.ServiceCore/Services/CacheService.cs +++ b/ZR.ServiceCore/Services/CacheService.cs @@ -1,11 +1,11 @@ -using System; -using ZR.Common; +using ZR.Common; namespace ZR.Service.System { public class CacheService { private readonly static string CK_verifyScan = "verifyScan_"; + private readonly static string CK_phoneSmsCode = "phone_sms_code_"; #region 用户权限 缓存 public static List GetUserPerms(string key) { @@ -59,5 +59,23 @@ namespace ZR.Service.System } return 0; } + + public static object SetPhoneCode(string key, string val) + { + var ck = CK_phoneSmsCode + key; + + return CacheHelper.SetCache(ck, val, 10); + } + public static bool CheckPhoneCode(string key, string val) + { + var ck = CK_phoneSmsCode + key; + var save_code = CacheHelper.Get(ck); + + if (save_code != null && save_code.Equals(val)) + { + return true; + } + return false; + } } } diff --git a/ZR.ServiceCore/Services/IService/ISysLoginService.cs b/ZR.ServiceCore/Services/IService/ISysLoginService.cs index 15d6ba8..b3ac59b 100644 --- a/ZR.ServiceCore/Services/IService/ISysLoginService.cs +++ b/ZR.ServiceCore/Services/IService/ISysLoginService.cs @@ -1,8 +1,7 @@ -using Infrastructure; -using System; -using ZR.Model; +using ZR.Model; using ZR.Model.System; using ZR.Model.System.Dto; +using ZR.ServiceCore.Model.Dto; namespace ZR.Service.System.IService { @@ -15,7 +14,14 @@ namespace ZR.Service.System.IService /// /// public SysUser Login(LoginBodyDto loginBody, SysLogininfor logininfor); - + /// + /// 手机号登录 + /// + /// + /// + /// + /// + SysUser PhoneLogin(PhoneLoginDto loginBody, SysLogininfor logininfor, SysUser user); /// /// 查询操作日志 /// diff --git a/ZR.ServiceCore/Services/IService/ISysUserService.cs b/ZR.ServiceCore/Services/IService/ISysUserService.cs index a074e85..faf5bd1 100644 --- a/ZR.ServiceCore/Services/IService/ISysUserService.cs +++ b/ZR.ServiceCore/Services/IService/ISysUserService.cs @@ -81,6 +81,6 @@ namespace ZR.Service.System.IService SysUser Login(LoginBodyDto user); - void UpdateLoginInfo(LoginBodyDto user, long userId); + void UpdateLoginInfo(string userIP, long userId); } } diff --git a/ZR.ServiceCore/Services/SysLoginService.cs b/ZR.ServiceCore/Services/SysLoginService.cs index 2a3dbd7..4f4d63e 100644 --- a/ZR.ServiceCore/Services/SysLoginService.cs +++ b/ZR.ServiceCore/Services/SysLoginService.cs @@ -1,6 +1,5 @@ using Infrastructure; using Infrastructure.Attribute; -using Infrastructure.Extensions; using Microsoft.AspNetCore.Http; using UAParser; using ZR.Model; @@ -8,6 +7,7 @@ using ZR.Model.System; using ZR.Model.System.Dto; using ZR.Repository; using ZR.Service.System.IService; +using ZR.ServiceCore.Model.Dto; namespace ZR.Service.System { @@ -64,10 +64,40 @@ namespace ZR.Service.System logininfor.Status = "0"; logininfor.Msg = "登录成功"; AddLoginInfo(logininfor); - SysUserService.UpdateLoginInfo(loginBody, user.UserId); + SysUserService.UpdateLoginInfo(loginBody.LoginIP, user.UserId); return user; } + /// + /// 登录验证 + /// + /// + /// + /// + /// + public SysUser PhoneLogin(PhoneLoginDto loginBody, SysLogininfor logininfor, SysUser user) + { + logininfor.UserName = user.UserName; + logininfor.Status = "1"; + logininfor.LoginTime = DateTime.Now; + logininfor.Ipaddr = loginBody.LoginIP; + ClientInfo clientInfo = httpContextAccessor.HttpContext.GetClientInfo(); + logininfor.Browser = clientInfo.ToString(); + logininfor.Os = clientInfo.OS.ToString(); + + if (user.Status == 1) + { + logininfor.Msg = "该用户已禁用"; + AddLoginInfo(logininfor); + throw new CustomException(ResultCode.LOGIN_ERROR, logininfor.Msg, false); + } + + logininfor.Status = "0"; + logininfor.Msg = "登录成功"; + AddLoginInfo(logininfor); + SysUserService.UpdateLoginInfo(loginBody.LoginIP, user.UserId); + return user; + } /// /// 查询登录日志 /// diff --git a/ZR.ServiceCore/Services/SysUserService.cs b/ZR.ServiceCore/Services/SysUserService.cs index 7d7a4c3..17d955d 100644 --- a/ZR.ServiceCore/Services/SysUserService.cs +++ b/ZR.ServiceCore/Services/SysUserService.cs @@ -329,12 +329,12 @@ namespace ZR.Service /// /// 修改登录信息 /// - /// + /// /// /// - public void UpdateLoginInfo(LoginBodyDto user, long userId) + public void UpdateLoginInfo(string userIP, long userId) { - Update(new SysUser() { LoginIP = user.LoginIP, LoginDate = DateTime.Now, UserId = userId }, it => new { it.LoginIP, it.LoginDate }); + Update(new SysUser() { LoginIP = userIP, LoginDate = DateTime.Now, UserId = userId }, it => new { it.LoginIP, it.LoginDate }); } } }