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