From bbd9fec7d95c42775a4c865531821cccb32fd6b5 Mon Sep 17 00:00:00 2001 From: Ky_Gyt <1971574843@qq.com> Date: Tue, 8 Jun 2021 10:08:58 +0800 Subject: [PATCH] =?UTF-8?q?=E9=AA=8C=E8=AF=81=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Api/Ewide.Core/Enum/ErrorCode.cs | 43 +++ Api/Ewide.Core/Service/User/Dto/Userphone.cs | 31 ++ .../Service/User/ISysUserService.cs | 3 + Api/Ewide.Core/Service/User/SysUserService.cs | 208 +++++++++- Api/Ewide.Core/Util/CodeHelper.cs | 252 ++++++++++++ Web/src/common/api/requests/sys/userManage.js | 12 + Web/src/common/storage/index.js | 3 +- .../system/account/setting/safety/index.vue | 131 ++++--- .../system/account/setting/safety/mail.vue | 360 ++++++++++++++++++ .../system/account/setting/safety/phone.vue | 360 ++++++++++++++++++ 10 files changed, 1341 insertions(+), 62 deletions(-) create mode 100644 Api/Ewide.Core/Service/User/Dto/Userphone.cs create mode 100644 Api/Ewide.Core/Util/CodeHelper.cs create mode 100644 Web/src/pages/system/account/setting/safety/mail.vue create mode 100644 Web/src/pages/system/account/setting/safety/phone.vue diff --git a/Api/Ewide.Core/Enum/ErrorCode.cs b/Api/Ewide.Core/Enum/ErrorCode.cs index d2d5f1e..dfb1429 100644 --- a/Api/Ewide.Core/Enum/ErrorCode.cs +++ b/Api/Ewide.Core/Enum/ErrorCode.cs @@ -122,6 +122,49 @@ namespace Ewide.Core [ErrorCodeItemMetadata("账号已冻结")] D1017, + /// + /// 发送验证流程错误 + /// + [ErrorCodeItemMetadata("发送验证流程错误")] + D1018, + + /// + /// 没有可验证方式 + /// + [ErrorCodeItemMetadata("没有可验证方式")] + D1019, + + /// + /// 验证错误 + /// + [ErrorCodeItemMetadata("验证错误")] + D1020, + + /// + /// 绑定失败 + /// + [ErrorCodeItemMetadata("绑定失败")] + D1021, + + /// + /// 验证码失效 + /// + [ErrorCodeItemMetadata("验证码失效")] + D1022, + + + /// + /// 请不要频繁发送验证码 + /// + [ErrorCodeItemMetadata("请不要频繁发送验证码")] + D1023, + + /// + /// 请不要频繁发送验证码 + /// + [ErrorCodeItemMetadata("验证码发送失败")] + D1024, + /// /// 父机构不存在 /// diff --git a/Api/Ewide.Core/Service/User/Dto/Userphone.cs b/Api/Ewide.Core/Service/User/Dto/Userphone.cs new file mode 100644 index 0000000..8335f78 --- /dev/null +++ b/Api/Ewide.Core/Service/User/Dto/Userphone.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Ewide.Core.Service +{ + public class Usermailphone + { + /// + /// 绑定的值 + /// + public string Target { get; set; } + + /// + /// 发送验证码类型,1为发送给原邮箱,2为发送给原手机号 + /// + public int? Type { get; set; } + + /// + /// 第一次验证码 + /// + public int? Orgcode { get; set; } + + /// + /// 第二次验证码 + /// + public int? Code { get; set; } + } +} diff --git a/Api/Ewide.Core/Service/User/ISysUserService.cs b/Api/Ewide.Core/Service/User/ISysUserService.cs index 7066ff0..a3bc80d 100644 --- a/Api/Ewide.Core/Service/User/ISysUserService.cs +++ b/Api/Ewide.Core/Service/User/ISysUserService.cs @@ -26,5 +26,8 @@ namespace Ewide.Core.Service Task UpdateUser(UpdateUserInput input); Task UpdateUserInfo(UserInput input); Task UpdateUserPwd(ChangePasswordUserInput input); + + Task SendCode(Usermailphone input); + Task CheckBindcode(Usermailphone input); } } \ No newline at end of file diff --git a/Api/Ewide.Core/Service/User/SysUserService.cs b/Api/Ewide.Core/Service/User/SysUserService.cs index c84a930..54dcfd3 100644 --- a/Api/Ewide.Core/Service/User/SysUserService.cs +++ b/Api/Ewide.Core/Service/User/SysUserService.cs @@ -1,4 +1,5 @@ using Ewide.Core.Service.Role; +using Ewide.Core.Util; using Furion.DatabaseAccessor; using Furion.DatabaseAccessor.Extensions; using Furion.DataEncryption; @@ -8,9 +9,11 @@ using Furion.FriendlyException; using Mapster; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Caching.Memory; using System; using System.Collections.Generic; using System.Linq; +using System.Text.RegularExpressions; using System.Threading.Tasks; namespace Ewide.Core.Service @@ -23,15 +26,16 @@ namespace Ewide.Core.Service { private readonly IRepository _sysUserRep; // 用户表仓储 private readonly IUserManager _userManager; - private readonly ISysCacheService _sysCacheService; private readonly ISysEmpService _sysEmpService; + private readonly IMemoryCache _iMemoryCache; private readonly ISysUserDataScopeService _sysUserDataScopeService; private readonly ISysUserRoleService _sysUserRoleService; private readonly ISysUserAreaService _sysUserAreaService; public SysUserService(IRepository sysUserRep, IUserManager userManager, + IMemoryCache memoryCache, ISysCacheService sysCacheService, ISysEmpService sysEmpService, ISysUserDataScopeService sysUserDataScopeService, @@ -40,13 +44,14 @@ namespace Ewide.Core.Service { _sysUserRep = sysUserRep; _userManager = userManager; + _iMemoryCache = memoryCache; _sysCacheService = sysCacheService; _sysEmpService = sysEmpService; _sysUserDataScopeService = sysUserDataScopeService; _sysUserRoleService = sysUserRoleService; _sysUserAreaService = sysUserAreaService; } - + /// /// 分页查询用户 /// @@ -448,5 +453,204 @@ namespace Ewide.Core.Service throw Oops.Oh(ErrorCode.D1013); } } + + /// + ///发送验证码 + /// + [HttpPost("/sysUser/SendCode")] + public async Task SendCode(Usermailphone input) + { + var Orgcode_Key = "ewide_Orgcode"; + var Smscode_Key = "ewide_smscode"; + var Mailcode_Key = "ewide_mailcode"; + var Regex_phone = @"^((13[0-9])|(14[5,7])|(15[^4,\\D])|(17[0,1,3,6-8])|(18[0-9])|(19[8,9])|(166))[0-9]{8}$"; + var Regex_Email = @"^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$"; + CodeHelper ch = new CodeHelper(_iMemoryCache); + //Type为1时,给原手机号发送验证码 + if(input.Type == 1) + { + try + { + return ch.SendSmscode(_userManager.User.Phone, Orgcode_Key); + } + catch (Exception e) + { + throw Oops.Oh(ErrorCode.D1018); + } + + } + //Type为2时,给原邮箱发送验证码 + else if(input.Type == 2) + { + if(new Regex(@"^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$").IsMatch(_userManager.User.Email)) + { + try + { + return ch.SendMail(_userManager.User.Email, Orgcode_Key); + } + catch (Exception e) + { + throw Oops.Oh(ErrorCode.D1018); + } + } + throw Oops.Oh("原邮箱错误"); + } + //Type为null时,则发验证码 + else + { + //通过正则判断绑定类型 + if(new Regex(Regex_phone).IsMatch(input.Target)) + { + try + { + + ch.SendSmscode(input.Target, Smscode_Key); + return true; + } + catch(Exception e) + { + throw Oops.Oh(ErrorCode.D1018); + } + } + if(new Regex(Regex_Email).IsMatch(input.Target)) + { + try + { + ch.SendMail(input.Target, Mailcode_Key); + return true; + } + catch (Exception e) + { + throw Oops.Oh(ErrorCode.D1018); + } + } + throw Oops.Oh("格式错误"); + } + } + + /// + ///检验验证码并且绑定 + /// + [HttpPost("/sysUser/CheckBindcode")] + public async Task CheckBindcode(Usermailphone input) + { + var Orgcode_Key = "ewide_Orgcode"; + var Smscode_Key = "ewide_smscode"; + var Mailcode_Key = "ewide_mailcode"; + var Regex_phone = @"^((13[0-9])|(14[5,7])|(15[^4,\\D])|(17[0,1,3,6-8])|(18[0-9])|(19[8,9])|(166))[0-9]{8}$"; + var Regex_Email = @"^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$"; + var user = await _sysUserRep.FirstOrDefaultAsync(u => u.Id == _userManager.UserId); + CodeHelper ch = new CodeHelper(_iMemoryCache); + if(input.Type == 1) + { + if (ch.Checkcode(_userManager.User.Phone, input.Orgcode, Orgcode_Key)) + { + return true; + }; + throw Oops.Oh("验证错误"); + } + else if(input.Type == 2) + { + if(ch.Checkcode(_userManager.User.Email, input.Orgcode, Orgcode_Key)) + { + return true; + } + throw Oops.Oh("验证错误"); + } + else + { + //为第一次绑定 + if(string.IsNullOrEmpty(_userManager.User.Phone) && string.IsNullOrEmpty(_userManager.User.Email)) + { + if(new Regex(Regex_phone).IsMatch(input.Target)) + { + if (ch.Checkcode(input.Target,input.Code, Smscode_Key)) + { + try + { + user.Phone = input.Target; + await user.UpdateIncludeNowAsync(new string[] { + nameof(SysUser.Phone) + }, true); + return "手机绑定成功"; + } + catch + { + throw Oops.Oh("手机绑定错误"); + } + } + throw Oops.Oh("验证码失效"); + } + if(new Regex(Regex_Email).IsMatch(input.Target)) + { + if (ch.Checkcode(input.Target, input.Code, Mailcode_Key)) + { + try + { + user.Email = input.Target; + await user.UpdateIncludeNowAsync(new string[] { + nameof(SysUser.Email) + }, true); + return "邮箱绑定成功"; + } + catch + { + throw Oops.Oh("邮箱绑定错误"); + } + } + throw Oops.Oh("验证码失效"); + } + throw Oops.Oh("号码格式不对"); + } + else + { + bool CheckOrgPhone = ch.Checkcode(_userManager.User.Phone, input.Orgcode, Orgcode_Key); + bool CheckOrgEmail = ch.Checkcode(_userManager.User.Email, input.Orgcode, Orgcode_Key); + if (CheckOrgPhone || CheckOrgEmail) + { + if (new Regex(Regex_phone).IsMatch(input.Target)) + { + if (ch.Checkcode(input.Target, input.Code, Smscode_Key)) + { + try + { + user.Phone = input.Target; + await user.UpdateIncludeNowAsync(new string[] { + nameof(SysUser.Phone) + }, true); + return "手机改绑成功"; + } + catch + { + throw Oops.Oh("手机绑定错误"); + } + } + throw Oops.Oh("验证码失效"); + } + if (new Regex(Regex_Email).IsMatch(input.Target)) + { + if (ch.Checkcode(input.Target, input.Code, Mailcode_Key)) + { + try + { + user.Email = input.Target; + await user.UpdateIncludeNowAsync(new string[] { + nameof(SysUser.Email) + }, true); + return "邮箱改绑成功"; + } + catch + { + throw Oops.Oh("邮箱绑定错误"); + } + } + throw Oops.Oh("验证码失效"); + } + throw Oops.Oh("号码格式不对"); + } + throw Oops.Oh("验证码失效"); + } + } + } } } diff --git a/Api/Ewide.Core/Util/CodeHelper.cs b/Api/Ewide.Core/Util/CodeHelper.cs new file mode 100644 index 0000000..a8851f2 --- /dev/null +++ b/Api/Ewide.Core/Util/CodeHelper.cs @@ -0,0 +1,252 @@ +using Aliyun.Acs.Core; +using Aliyun.Acs.Core.Exceptions; +using Aliyun.Acs.Core.Profile; +using Furion; +using Furion.FriendlyException; +using Microsoft.Extensions.Caching.Memory; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Mail; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; + +namespace Ewide.Core.Util +{ + public class CodeHelper + { + private readonly IMemoryCache _IMemoryCache; + public CodeHelper(IMemoryCache IMemoryCache) + { + _IMemoryCache = IMemoryCache; + } + public static string Aliyun_AccessKey = App.Configuration["SmsHelper:Aliyun_AccessKey"]; + public static string Aliyun_AccessSecret = App.Configuration["SmsHelper:Aliyun_AccessSecret"]; + public static string Aliyun_Smscode_SignName = App.Configuration["SmsHelper:Aliyun_Smscode_SignName"]; + public static string Aliyun_Smscode_TemplateCode = App.Configuration["SmsHelper:Aliyun_Smscode_TemplateCode"]; + + /// + /// 发送验证码间隔时间(秒) + /// + protected static int code_Countdown = 60; + + /// + /// 验证code + /// + protected static string Orgcode_Key = "ewide_Orgcode"; + + /// + ///邮箱Code + /// + protected static string mailcode_Key = "ewide_mailcode"; + + /// + ///手机Code + /// + protected static string smscode_Key = "ewide_smscode"; + + /// + /// 将code存入缓存 + /// + /// 验证方式 + /// 验证码 + /// code的类型 + public void Updatecode(string num, int code, string way) + { + + var ts = DateTime.Now - new DateTime(1970, 1, 1, 0, 0, 0, 0); + _IMemoryCache.Set( + way + "_" + num, // code的类型_手机或者邮箱号码 + code + "_" + (int)ts.TotalSeconds, // 1234567_TimeStamp + DateTime.UtcNow.AddMinutes(5)// 5分钟过期 + ); + } + + /// + /// 判断是否含有code + /// + /// + /// + /// + /// + public bool Checkcode(string Target, int? code,string Key) + { + var cache = (string)_IMemoryCache.Get(Key + "_" + Target);// 获取匹配类型的code值 + if (string.IsNullOrEmpty(cache)) + { + return false; + } + return code == cache.Split('_').Select(p => int.Parse(p)).First(); + } + + /// + /// 判断是否含有code + /// + /// + /// + /// + /// + public bool Checkhavecode(string num, string way) + { + var cache = (string)_IMemoryCache.Get(way + "_" + num);// 获取匹配类型的code值 + if (string.IsNullOrEmpty(cache)) + { + return false; + } + return true; + } + + /// + /// 删除相应缓存 + /// + /// + /// + public void removecode(string way, string num){ + _IMemoryCache.Remove(way + "_" + num); + } + + /// + /// code60秒才可再次发送 + /// + /// 验证类型 + /// 值 + /// + /// + public bool IscodeCountdown(string way, string num, int coundown = 60) + { + return GetSmscodeCountdown(way, num) < coundown; + } + + /// + /// 发送验证码倒计时 + /// + /// + /// + public int GetSmscodeCountdown(string way,string num) + { + var cache = (string)_IMemoryCache.Get(way + "_" + num); + if (String.IsNullOrEmpty(cache)) + { + return code_Countdown; + } + var ts = cache.Split('_').Select(p => int.Parse(p)).Last(); + var nowTs = (int)(DateTime.Now - new DateTime(1970, 1, 1, 0, 0, 0, 0)).TotalSeconds; + return nowTs - ts; + } + + /// + /// 发送手机验证码 + /// + public bool SendSmscode(string mobile, string way ,int length = 6) + { + if (!new Regex(@"^((13[0-9])|(14[5,7])|(15[^4,\\D])|(17[0,1,3,6-8])|(18[0-9])|(19[8,9])|(166))[0-9]{8}$").IsMatch(mobile)) + { + throw Oops.Oh(ErrorCode.D1011); + } + //if (IscodeCountdown(way, mobile, code_Countdown)) + //{ + // throw Oops.Oh(ErrorCode.D1023); + + //} + + IClientProfile profile = DefaultProfile.GetProfile("cn-hangzhou", Aliyun_AccessKey, Aliyun_AccessSecret); + DefaultAcsClient client = new DefaultAcsClient(profile); + CommonRequest request = new CommonRequest + { + Method = Aliyun.Acs.Core.Http.MethodType.POST, + Domain = "dysmsapi.aliyuncs.com", + Version = "2017-05-25", + Action = "SendSms" + }; + request.AddQueryParameters("PhoneNumbers", mobile); + request.AddQueryParameters("SignName", Aliyun_Smscode_SignName); + request.AddQueryParameters("TemplateCode", Aliyun_Smscode_TemplateCode); + + var minValue = Convert.ToInt32("1".PadRight(length, '0')); + var maxValue = Convert.ToInt32("1".PadRight(length + 1, '0')); + var code = new Random().Next(minValue, maxValue); + var param = new { code }; + request.AddQueryParameters("TemplateParam", JsonConvert.SerializeObject(param)); + Updatecode(mobile, code,way); + try + { + CommonResponse response = client.GetCommonResponse(request); + var msg = System.Text.Encoding.UTF8.GetString(response.HttpResponse.Content); + var result = JsonConvert.DeserializeObject(msg); + var j = (JObject)result; + if(!"OK".Equals(j["Code"].ToString(), StringComparison.CurrentCultureIgnoreCase)) + { + throw Oops.Oh("请不要频繁发送"); + } + return true; + } + catch (ServerException e) + { + throw Oops.Oh(ErrorCode.D1024); + }catch(Exception e) + { + throw Oops.Oh("访问异常"); + } + } + + /// + /// 发送邮箱 + /// + /// + /// + /// + public bool SendMail(string mail, string way,int length = 6) + { + if (!new Regex(@"^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$").IsMatch(mail)) + { + throw Oops.Oh("格式不对"); + } + if (IscodeCountdown(mail, way, code_Countdown)) + { + throw Oops.Oh("60秒后才能再次发送"); + + } + string account = App.Configuration["Mail:Account"]; + string passWord = App.Configuration["Mail:PassWord"]; + //var options = Options.Create(options: new MemoryCacheOptions()); + //IMemoryCache cache = new MemoryCache(options); + SmtpClient smtpClient = new SmtpClient(); + smtpClient.EnableSsl = true; + smtpClient.UseDefaultCredentials = false; + smtpClient.Host = App.Configuration["Mail:Host"]; + smtpClient.Credentials = new System.Net.NetworkCredential(account, passWord); + smtpClient.DeliveryMethod = System.Net.Mail.SmtpDeliveryMethod.Network; + + MailMessage mailMessage = new MailMessage(); + MailAddress fromAddr = new MailAddress(account); + mailMessage.From = fromAddr; + mailMessage.To.Add(mail); + mailMessage.Subject = App.Configuration["Mail:Subject"]; + mailMessage.BodyEncoding = Encoding.UTF8; + mailMessage.IsBodyHtml = true; + mailMessage.Priority = MailPriority.Low; + var minValue = Convert.ToInt32("1".PadRight(length, '0')); + var maxValue = Convert.ToInt32("1".PadRight(length + 1, '0')); + var code = new Random().Next(minValue, maxValue); + var param = new { code }; + Updatecode(mail, code, way); + try + { + var Message = "邮箱验证码为:" + code.ToString(); + mailMessage.Body = Message; + smtpClient.Send(mailMessage); + + return true; + } + catch + { + throw Oops.Oh(ErrorCode.xg1100); + } + } + + + } +} diff --git a/Web/src/common/api/requests/sys/userManage.js b/Web/src/common/api/requests/sys/userManage.js index 14c2968..0e90992 100644 --- a/Web/src/common/api/requests/sys/userManage.js +++ b/Web/src/common/api/requests/sys/userManage.js @@ -73,4 +73,16 @@ export default { * 更新基本信息 */ sysUserUpdateInfo: ['/sysUser/updateInfo', 'post'], + + + /** + * 发送验证码 + */ + SendCode: ['/sysUser/SendCode', 'post'], + + /** + * 绑定/验证 + */ + CheckBindcode: ['/sysUser/CheckBindcode', 'post'], + } \ No newline at end of file diff --git a/Web/src/common/storage/index.js b/Web/src/common/storage/index.js index 73e8b15..444d5c1 100644 --- a/Web/src/common/storage/index.js +++ b/Web/src/common/storage/index.js @@ -1,9 +1,10 @@ const SESSION_KEY = '__SESSION' const SETTING_KEY = '__SETTINGS' const GLOBAL_INFO_KEY = '__GLOBAL_INFO' - +const COUNT_DWON_KEY = '__COUNT_DWON' export { SESSION_KEY, SETTING_KEY, GLOBAL_INFO_KEY, + COUNT_DWON_KEY } \ No newline at end of file diff --git a/Web/src/pages/system/account/setting/safety/index.vue b/Web/src/pages/system/account/setting/safety/index.vue index 7777377..996254d 100644 --- a/Web/src/pages/system/account/setting/safety/index.vue +++ b/Web/src/pages/system/account/setting/safety/index.vue @@ -46,6 +46,9 @@ > + + + \ No newline at end of file diff --git a/Web/src/pages/system/account/setting/safety/mail.vue b/Web/src/pages/system/account/setting/safety/mail.vue new file mode 100644 index 0000000..a95237e --- /dev/null +++ b/Web/src/pages/system/account/setting/safety/mail.vue @@ -0,0 +1,360 @@ + + \ No newline at end of file diff --git a/Web/src/pages/system/account/setting/safety/phone.vue b/Web/src/pages/system/account/setting/safety/phone.vue new file mode 100644 index 0000000..bdab2e6 --- /dev/null +++ b/Web/src/pages/system/account/setting/safety/phone.vue @@ -0,0 +1,360 @@ + + \ No newline at end of file