From df36de52fa830dba1691d36a2d2e50995352f58e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=8D=E5=81=9A=E7=A0=81=E5=86=9C?= <599854767@qq.com> Date: Sun, 19 Nov 2023 13:11:29 +0800 Subject: [PATCH] =?UTF-8?q?:sparkles:=E6=96=B0=E5=A2=9E=E6=89=8B=E6=9C=BA?= =?UTF-8?q?=E5=8F=B7=E7=99=BB=E5=BD=95=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Infrastructure/Helper/RandomHelper.cs | 25 ++++++ .../Controllers/System/SysLoginController.cs | 78 +++++++++++++++++- .../System/SysProfileController.cs | 1 + ZR.Admin.WebApi/wwwroot/data.xlsx | Bin 37928 -> 38616 bytes ZR.ServiceCore/Model/Dto/PhoneLoginDto.cs | 25 ++++++ ZR.ServiceCore/Services/CacheService.cs | 22 ++++- .../Services/IService/ISysLoginService.cs | 14 +++- .../Services/IService/ISysUserService.cs | 2 +- ZR.ServiceCore/Services/SysLoginService.cs | 34 +++++++- ZR.ServiceCore/Services/SysUserService.cs | 6 +- 10 files changed, 193 insertions(+), 14 deletions(-) create mode 100644 Infrastructure/Helper/RandomHelper.cs create mode 100644 ZR.ServiceCore/Model/Dto/PhoneLoginDto.cs 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 6c84d585a033dd482cfc8c006490acb952625779..8fca81c6272cbf05668ed0a0feeb6a2c0c5ac7d3 100644 GIT binary patch delta 22752 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 SQ3?dxQAji=@p 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

sZBT%(A5hLk`X#BZlG2@HS$r_iF9Ea6{J*GQA zkN*vPUFOVV)P7Bc0{^Zqa-Wf%xoMX`M|ymwoxCXlqwtdVCHC!*!L!ZvrZoGz=-=bj?W%W4vPh z&NF3gObbW;-Ayd>MdfE(uC_GTz60Ta)6N4F6Pe@f#=>ACXv1I@=mqAIB2M22DWiKx zZ9;QF?PGltN>kD(8fsU&-|RG(_DKcnh={^$Nzb9NE$h*e6KQfLrvX|#fDC0Q^k>FfCkM zo!?$j)3}rxtqRi>`l9RgF-D;o8Iw@$%DC=Kzjb5Hsh3dr*xgN(n?q<fpqT(q?v?%qG z!DCnFf`ED;8Ji6OtLD#%iX*9B=h*6Bk`Rz`PV?C&AOk&kPWK!Rl~@!Rk^ZR36&{xH|lD(+XV*3To$d2u5w;QLvtx`pngt z3Iy~Ibv8@gmvcej<{KQCEhi)Z)DFe-#V*IlHvbk+sG$qVVYv-W*nJjIZov(j$bBf6 za-Su350mDJ@SjnW1lPIrpnYQ9sHx8|X-~(YQ^<n3AA?Jz@-^^~L^v-8b5xdS*2ujdmXtDiCZ zoKsEG{`4{7S1pc&_tMh+#h-*Y6^zUc)jGct`9taQ=bAZB_Xb^%kJaJY>}CE2c7yg} zk;P-H`Z`39s?FcXd2g;;EwmPE+EkwBJP&d;I-O6oM-&#|BfT#1(RsRJeG!a7QU3X0=TP^QT3 zW@EJ`6a2HI7bcDVTs%6tp7skCxcsvfB+)i?VjTHqTQPs+gBOsg>nwme_7^VR<1Aa_ ziM=TfVpIUhl6hqM0{(QbN`-EtOQFj94|-)x(>h6hPu7T&RRCO!YaFk+1@}1zr~W_!nSB zAy3-Ou0iWq%PhUPh=X2xnPr+6zdxav1j_5L5FJ!3&^o!e4}JbKy*Bk18z5(Sl{~gA zQ6+t25-^04jt@ph5;D>k6_X0Qx`ElFNWGGth(cMuKjDrE1WX)lSnJNia2Nu&*l0QL zPe>+$tS%&@*M3f~8A9#C3IK%^Fgj}pZ==Hh`&SR!%;!SRM0{gBvy^}qM?XQHYjtPN z%Zwtkqyv;rVNfcjDC5&B%l`=(>>-~iN^G8!1Xd-Et6UF8PVNnNw)x`_DgH64N_4>G zzbxtck)2gWmHbj*mx$J|w#&Vrd9v%r7CR?UOPgr2C9jXGCji)zM}8p&e0)mxV4JsBG4{@^#jM3{efEy z9*JL=|IRu6_s@tw7lRqI$DX__9uWn&mYrgkT>@>&s#T%=cS1+=mB-~5D`zC&2yE&{ zPS2@82THC0fNC=!(iAxtH`>jTf6V&u6H=j%z~D$O#-Hoy`U;dj4$m^MAN0chUnDvK z2x(H!^1;L8$vs?;L?*P_9Kp+yT9=RIBauIXK&$z>O2uK+md&Ms@iAq(Vj{(r&m>IF z#;vU2)Wj+m5@_!LYmq?83hDg{!0Kll`j(qPXQN&kkKM{Q(B?VGL(Vc&5K%BZbW;#l zDnYVjt_LF+#Z69_)>p>RVxbTWeW-|{JbsQknK zZJ?}m&|&=Ne{CE$dFMW7J_mXnn5+OUeHP!2+O7x^1NGPG04|c%RA}_G74tzc@nr?<2J4m3p#w>Ji7Ozxc`KvA zEsTABp|&Y5vozEQUO==mHZa}|bV$Nydp>Q5d3~G?;x>^AiMY#VEDh!?#vg<`Y&9uT zkBCR?h1a?33ddoZMi8NKGp|+$*%Yy`P()STFs2i{IYm67GK;?e`0O49l&N+G=N7K) z=DON})=9mP2Wo=KRdryqDTj2rUJC2lp5#BajS*2)7y4^7e9YW5`+*fU|Fuz(90Mb( zHnS43P~_sKeN?4=lm_B)mE}3ERr1snBiI+qd6D5r97{cHk$*9y)%hgSK}BZT<}+eU z#tzsTm(_SQnO$a?;J7{nmb|`d%YdwAz)WmHG?HZI(WA?>D`lDy2vB3q;JSM;iC%7Sq z!Yv+nS(Qa3DwvCAC5hxwU)Vu#5e9I*M(*neR{cwZl4L8-&Ck=IG#rJn1Et}<)iTk5 zWb{GQFfd>PQCI%~0eE~uOs$J!HUBIR;_S>Qu+w4I0mLw~SI(_*U`UU^SxkV&6Z{nP zmY73w!pVB!B{0e`UZZ;RpYu!hsr;L#AnXPpCg>@vjdX~KpaIp_h`Ik9Ds=PVeCUjS z2S9{I($8+`JZ<}nYWiuItb&$?1jnJv>{A|!>(5GFB%C>%t(s|2w`S-`@^TP5c}KY6 zv^u(t8Bi-<5rN1CR3A!OrCnHN&m2V3_||DIU}o)D70$fKM05% zp82q4#F=O?w=jo|d``!Unf|>t+gsh;6vQj$ltWjuJP{e~>R@mn8aGmH$ zFZ9ctoHrTs+g|p#H~)E_QONWrHn`T>^^aiBF}iwmtSD@Y zDBk5$8Yx1kz&Ws6%mF$pJX(hbW3vkXF^sP9+9na$xYoT?4^DCxBu=Ety z(iPG*9I^M8K#-S{=E4XU6rwKM**vJY!Kxk~t(6D(^*P4^uQlC=K2Caz{Zm{kqEF@S z24D))v=^Nl0tgQpP?5l6M0VtqvVvsor=}wZYAyTHKQ^Bq z2JrZW0~Qg;_#O`Z-xc7(HK#3_r@sBgK>Ucb59bypupusu5Oh-Jf@kHapRmQETLyRZ zEa^6VoihXl;uAW^B!U@dzUrzekVI(cH&6`;RW4Y3P~my}Be(C;{}bhf6hKvppz&kI z-EzuZy0(mtwiHNq7Cy0zSC2i9Z-Y+3JG>-@W43>V99A`hcHUHu0)%$%)nd&M$f(YD z-gM8?KmHmx_WUi3Cv2gJz~dG_B~IZ=U&Jy{U0`W90^xI?v`{tmXfn7$B{%X-$Ww zbsPOHp<9=9*CVawKVqH9^gprg2&IBHZyQn%2UP5HZ;fX%<)na=arQV&j*j0G=>Qr9 z$8uf~H{Z6t#kx-a%a;mEaazI*%rua8JYl-`(pZ|6$AL5~W*Er08OQSt^m}F;KzJFX zI3aTX*qq>MsbZMoRg?@k1WgZ$vRWG9lPn_FB}3a6*o|8Z2{=ZNIds{7r*(#lWrEhn zc?s$N?`$a=iC_H81+@VTxnAo`5q0d#d?T0nINHn_0hY5$AIK(ekftC;#(3L!OnTgA z9En&Kp#e6g$9>+9%kmqogP{$t-2LyM(@^5e41c$sn^K{WnAS|7$zPwwsV{uB#SqH5 zLRHB%m-G!uWZ=I6#rpvvy5KH-Uw_5d>SMcLx>zI>yZ4pSxkL?LO?G0;X(al;j+254 z4p(5E4j6y^`z(&7emC;~y+NK;?T3G?>;NHF=tiOR{g0Vfh1xRu14wK?T z>w8XoCYJuX)z!^kS)_}xwK>`wpo4gL(kRp31cn{UTl{)1uoABHvhM}dG|xRRmVQTG z?t*^me}uHM@2mR+p!rr_wGgHQMP7tY`Ap8ZdqpqivAtBVV^JtJSkDPDqw8%D*%SOW z`x%CB?5p#5;-eEYgPn}|qR*bz<_KBQj@IVrzEb?|zE@jBt`q$(?vT>HsB5zWdd$7- zO}dFjOrVujAZrIg%pEk52yIML=XgH(&{(Pqmf)_|AWKX>U^LV-ROq!;+SH9n+w3~R z>J``2e5q=@Ccwl;H#9E7T(-pbo3?ZdB}PPRA>x`RK&+lEOU*bd_Ab;BuKc+kJ0fti&ibcsCp{LO??NnPjuVpl*Kq=l;~{zH z8DD3rsUh!5!L*2{N1+$)lcA>-MiqXY-y3KwRu>jtqQJR7W4!Ge$;mD!hk11Hlh18I zjhxd{Ol=-&POAhd+YwC0<>47grLR(6_T*n*NaXet0C-#%iFCQDie)Yc!Ex5Omapko zJIq=dd02ur5O_l&;A(A3B*RXNR3Z!=4zG`ju&$6Q^<95%%KFZ0{7ty7!ZAe;se@Tb6FuqNLIjvENbkK`DmFC1|r(?JHHrniYi0#kO1gQ~fs+EKy zyy8aS0xqyaJdE*}W6au}zG-;sc_RxEwv7B_9Q(OQdOHDhQJ9~oiQoN{tXKh)x|77S z)@MGv6np1NwjJ(U#e_uX6t&yneXR&&4d_j&e0S&UF#^o%3Z`ArA>7acGt3GR@cI9d&nKqOzX{m=?)eQZI5& zH0jN@ToLQZ0Ly-AVurud45gA0ciB5}0AeX!;recks-N}gU^Zg=%n>vCtKZy7(Q{Q# z$pJHym333g5uxNP=d{<0?%p=c9w@JTD&J=Oas(Bi6xs9c8}&hH{Y$HGDY8$0ew)Y= zv|`6}<8FnJ6HQvPcZXpYHy|8`CdEO>uAodxDBB(bI!Wl(INSnL^E2tr)lu}FahRrn z!m$wf*HZ42&dV|HbwBpAX!pN?jS}^p0A8R~Kbf{SDe?Q(D8a8?1C)B0ieb@gdt|i~ zz?kHL0jSYr39gr8#Rw5 zj;EnLuaB=U54i`mf}rLF0MtJL{I@r0tJJ43z>M^oJnXJj$pSOSYbBBaghV$+f0%Bs z7RP~I3`YLhvs>9BHbpo;QWSo8Q-4RwBNe$~VJ9{d5-4kJ% zG72Ebtj%CD2A(|n#fbxUUFSQBLhL6*xWB#Iki}n6%A5C;;_)xn0U>4=>NM;%fF8?X z`*ZUf-MZOl&%;apQhZ~{VomR>bw7dDW2G+zNZ&Ax$pol0Ow#_!8qxX$#_HoEbe+^` zoOE$^h{*H(8NIr2g*i`F_Y^F^_6bgy#vG?uT~HuxYiZ;AP1b%$F&fi7elNg17bNU+ zts`Wrd{s2qA~lw5hvb3^Gs)9Md6i>G{ZFfTGl3=OOqL8($L8KEHwN)n=w z&(CmG0K9&KF`#zRXAdruhvX3mC`6r`%BAu$;i`eSgo2MAv)cE`WcgzXt<&mnmEV^< ztHr?ow!-nf8d~|5T(6ND37}dMY<(CWx9o*mLZ2Me7O#sOEhtR?yWixsMy0xA8rTV2 z8oX}1DulmIZd`1=9}DWaGc+E!npA1cFFp8w5lIOA^`)PyyXfe^fz^t#+NtCg@^ho= zM9W$1avHoR<@!AUC*;d`zk)05V?@MP4RjKH?HM}yH&Xhp{`RwmWq<}2&ufd;XK&1j zoB(b7Az^ezPiFlfD`o(p0K?95q1uXd>9lg$M7VsnBh_%(lKx8ly_Ll+p1^>3;A;LlbiQ8lNTbOrN~dk*5-ZS zv?oAEllVe5Alh{jFdVT@`#71_Rq)EBJgDCeTVN_NN`(&})VIU;_0i(WdzO~h?`Ae5q9`#)%rMBVXjx-# zamw|t7&%F+Rciwbtuu^bPHL_oL(IaNH!K=CW>l>dj);NEu{dnTOZZvemYhxYA8>%f zddl+#xWiy7us}OuyzI|xT%=OFB+_cLijdte{Xlq}pZ+`Gix+&$FO?;~D6;<8w_KPQ zxyJsKpv9Y{kRfLnO|7nvalpLyk-@{68QG5CPB$2fn!2F1qRF~zM#e}wG6P*H!2#xd zTPu2bBQaKyAR^qW^LJ3Yucs`Y%S;Az89l+)FC!CtO{IHiC?DSB&t25c{`#xZyE{U= zD`!x=Pis8Fv%ee5wSVcBM4J&moR8lHi^r1gnvy|tIcs{~7-`x8Uh>_Z5ZQB0tAl__ zzfK__2o~B{O5*30vtMnVn)tD8NctOF#Q?7K54O$N+P5F$m#8=#)7M=HwfBzzqP=Fs z#{uS3+c1gcESpORw(#OFJO%II|M#v)ik^KSGg?qBx|83I6DfdPD%$yzSp0L626*w= zS%;B4^-R@oAKAf5Jc0|0j^oV^du}dKRn5iN+WEUQGTpn}iQ-g)(6s7qW%je}vxBue zJ9oSoZ4Ye=*ZB=u13vp|2q;mF?U2x}mQrE0qyw z9=(&F>I%Utb`(z-+bu5Omgd0JKSiBOUQT0M26?Q#VD}vYlv~4Krj+pdEY&=iQ;cDB z5l;F`)oO($1K>{)7kdi;$ON4}4J60tkX}xR<6UbcMj3k-%K5Gzn_kk-q zTYW-7-BFQy5Z#{*AS50wPBWLw(j}XLY{zVj`#k1kc`9`q$M}ogl$>bL30;q?*~zSw zk58)$OQ1@Ymql;n=MP{GG4T>bsKAKxBR*=}VZL6(k&If(+#?Ogd(*dMpYwxFsc!f>lGY;;C3!dqoQerg=Xil39nZF zUK-gTd!3f+dB3E#yUHwAliA+h-VLByOdM5X^>s;A{z`}^(Z5?i}zkNdLCn*DoQ zxU&7&A@=K7UQ~Ep05{$@0IXIgkSb! z?#K2WEX@d{!x&|Cwy|l5BtnxyQ4Y=B>o~_pD^f$adQlUZ#p4mmQlav5Ai%)rq5oY8 z;{10dXioOy+8fxGSMeL{n7c9QGC!KBIH&l!u6k1!pE;by-@N7D@c}37sI4x#fB0ZKiS=hAikNk*v?6T z+tA(P%e9iCV4|k=IG7#OW?)Y_W^}A~M+VIkp%avwa-&jH>X=^WnhGpkG2Y zBTT%J+4uorMa7N_t5i`+=BZgEV+M85yE{|@3-6eTa%q8qr>K_-#U!#c0M9nV*4hYe zO9UONWox-*R+8s~)S$w52Zd)hkU*VqZLr8i1;9cVI3V?3x%qJ^A&|)pzIUfV4%_dr z_Dt3O;9$SxlC-vb)DOPEzkkHqYes%-k*llfW@>ougkh>9W4SDodrQ{0o%B{Kg6aAqapT=*Al+^j38oqmU&H zpd_yBv6j0LD&X_Yo*ccC%Y#OGR1SuLH`(+B`e<9rpl_3%=7OGH+U`TG`*<*B&TBd9 z!aA}Ud1vEqSGF~S(FM-4{4sH6IB}y4-Lqk=4y=kl3vT1ndzG>QM|L#t^NkJn`iJ(GN2ul31N{M{e;mV zAal>M6B9*w0hIy8H33HIl$zgbIKMHpt~fXCSr4hj^UwR;U#>~*)x}FW2=~Ga8?EXS z_rtUz^w^;i@u{w7;X0mzRh=7_Q-D#yBI5oIsbtA|%r~*9_|h8=>1dn*Gv^20KCNa&E26CA2;qZk5=#W6|!dj?y4wYXi`nlj}3N94sw7-+PTh;%p6&h z%)HtusQaRB^ar;)*dzz@n@!vR%s0cs)`)zz7`P~Pm~Y%*IQY0V>Is3+QTe;HA(PzX z%o=!(M_}Kq4O?BLQLvdqVk&WYcf)$M%zDXP(y8+$LKKol2aE^T{G<9Pv|n|}?`p>D zvtjZRC&h?GM`CE8@Jw3U&nI8@Z!s8mW3ej}KXHR4hu=vA7(wC9fxc-qKoQ<%g|@2y z6Y@pY%-G3;hGm2Y zX;COQft+>r{(e1~JduqN{$^r9spGHP6Wj4nZtB>4ZcVAl`_-G*ol}`y6Fwk&L*nTU zSgiqw63EW={d<>sUZgWA`GKE|_~WDc-^9OD_@$c|iI%N>daU}$Q^Dq^IT5=ahE$`)%)9$6s5f|zH( z5^1y{1O8OoIuJ*kA?PTCFucu!DER%foinAH@7va@+Pr~5UWRPsF_9zk4M<2Xf%*$p zOxMq{)Oya?37yQPJ|6<9Y_>zcu@nu5AvA0T1F?xg$o7laiZVcfTr7;i^^>~J;QxL* z!0E^61|7EUO+SzjxvT9glis;WnBLc9U8Zaj=vf1*(&muumV;(}uDfNq|G(W{7YdLmdf3cC)A{H1ZqUGYhA-vde{c#rL)2AQKQGqn~<@ zNurFnm+{21O`yJd>)!N5z$cVZat@7u4HYEH;zuo8|5H;*y_x@~GbW@&G4(x91VWVH z5UbRp_2}es{vP$C4uB@3)+*CzT#@Mq2MB+ZYvQ#5-hSwH@+-sjYR75cNh5x%tNaRU zZn+35N4WVl$mg-i_Jf$<+cWME&8_Oi`s=;=+Ej&#`15IPc#eXky0>NtiNb}Tk=%es z_T=$pQL_7h$q`*OWx}D)y#|RMkC!G2z>03pBE5U$R=h;G=nv^!q#RxPpj`zWfMQOe z$h?}t>iu1`j4ecoX%#j+g(eL4xC3RJv#xqQqg9r#NW{S6P*CCfJx3Oi>XKSscjnDP zUXLzDdOs;|k;ta$VWv2VnodcXq7T=zBMaj<$iOf;Ws>sg7QAP-8QA0$nq$??={rWK>vICn5h4|9|jKw zhR>3?DTALFr0@w4cM&*2hN(1cJ-1$_?4^9?ah67=)t9z1DGhq!fV6F(6Spo*=0;k2 zf0$a@U0zq_*dUHdZfB6%V%rg>bD>$47w!h=DyZye53MF}wFd`e^(J&Yt#UO6a_Z_r za-+gmGf16%OI5WNM~=`naG&0hDvsVk5u6BFnFM2kjkg9^4r_^Er83~b|M!Nk$Fy;2 zo-4&qp#h>l4mQinZ1Ul{juDw>HJNbLykb>m6zf)g=!)UqO;%0YCom^pVOVfBX11_) zh_dZM-Zd3u5DKenvSLkn|9I>9O0bRo3U<+Zmro-tfw^zk?6pvPY)=X}3dZ_6P@vhL zfLLV!U`)I))sEYgzUDSNV$G3bOOf08EdM1^GHN5(37Ex*s~hwH81~kXS=;WWof1-y zdm(+tEe@Kr5QTh=bF5=Bp1Qizv=rhNO7!zkaL>(izR0)w;Uv-7ZL+B2;B|!Xg@wI7 zu!w8Be?%Z^PV)-0<85(kb;weNsfKCyJIq)sv4l!^MIok~^h*g3nuXs_2R&8MMBH8?f5#?WANk+V~CBa87ckRddG&-01vAl4gEG zZgKa0iJWk+^kLf9f;2Vr)W5dqC*G`NLp$F=1>OH#eAk(=-{nU7n=CH4Wv$x`NThFv z=4@uNy2cW3=@mF*XcoUXzld%4oyJyV_gGCSki?!}p1&8`(xY#%`Eu}ZFci88c)!_y zXz92w1^9S*zCOL3)jU$H34HDRY0(pDK<{w?L-A(na*+;qYt*;3ah7w}H?F2;jmz6v%H8yTI$he3JGO)uy#e}C*aH>WKqLN#bM)U#?*ARE31ShRZt-r3`iUl zqUPoN()K#zJB)Gn597<2Q1{9lU?;v@wBN3k?|?i* z9+6p|mEH>PA<`@l+6K6zF^337N$qC$tvbIIUva-PlWTgO*+s%5{yl5HY$15s?c&hp zdH-Cu6T2|?1?hmq&|{I;zh(dLoPH+Y+yf_zLX#T=xAN)T^hE@`#0yqn44Ll#P3(;j ztZ{@Nc!I?pAd%Sn3t2nT!%lnvdih-1WycZ?`HjFyaH8sRB9}%zw(*vT)uKm?dHNMO>zSWEX@@gyT9uIO1!>PF(=&zEI2s?gZ1SoA zshm=^uMat+vG=qNGvQ)S;um9_T(!pUsHof?(1p=RHFjFgU~VoUWlda!Z-sYR5stwsyq@cN;(64fTJL( z@=ilJwJXbGbd^LlWQ>E{f2QBw2Roq`({1>R2%B*<950!Xg%#_1I?D2U<;{?-U& ztdSc!Dh%NUweeOnf_2v7n&>ZY(*AG+odmb-HX&gCrPY@Hu35E3Df2m!)&nQ7a(u^& zmV^Urbc7yrHH5x1c&kPr7GO2ppRPdNc?AXCBcsvHWe9CR$+yW8shucZwjqh^=RB;_ ztttfcL4MVR&N#|)St{D9ENYwR@@`gzzW7;%;5~L06N?YF9|sMwc^eYeZ2F`L=V$#V z2D?%oK|hC>5`ym0wp6nqvTxqT(ro}Uxa*a>9T?og&@LXKH|T?`urE8#nzs8P>YSOePv;?aX8vQKPP%VwjzYU?OYjUx0Adw= zH`D}mHyBtFCE~h-#daD;V)D=)grcworZ>pa{Uc?GhJY!z$tv7$@xP*hAVbx;{+c1e zG}+dP4PB^mT3rI-)Y)tNnTR7>R_MSI72oyD3O-jT)L22N8xO#!q>%8OD!z--ZFH-f#zykB$WA5=3C zC=UcG@92GIZ{4hMG1eGk&E0=016M|NaxKWah0L0<8!_~WFtr44myu9$@Fws<(oS_o zuqp@ zhN4>3rZDGBoFY8(^d|R*VockZcwnMeRkL9)`Nrs!@#4mG@Ti$k+l@LsVb}cx{=PrL z|3Mv|%(R_0J*LO{Len!RSp7_MtZJo#W^bt<^OF$*0mT=x-ahd?WO#pk90tFEqxbWl zfR-kCW_pDuH%1n-RY zYr$4vR7FK0&T1y&u*cdie*yOa152!yZ)_aajfF*ELfK4|2K~k+4wq>!ZE9o>gp7jl6#k7awO}TF3R^5Rh|dhKXZsb2n`cSQTt8vy#3W(bJ&Ulo6=k*4l`RS zGRI=mHxc;!b9g--?F=l)QWfCxyM5V1UzZSm=q%bc%r8-fHipAi{j4dPywx@u-9rEH zMK;!icG|CQ`&SS6wpP~}eoI_uUj{LC;j?Ld+(6{Sn2uC1DpJ|Ka1dfCY6`8(4-UNg zS;bRF9qQ##nN6^oFO?D4d#dd=4rkjt0Z_w=q6o%QsI}e~^BD2uitzwg>)FtTq6Co^ z1v0*TRZIwFjDQJUG5~rTPXJPEOGDA?vS%;SXYlKWqBuEPNzr3cXAN@ZF9Xr2fWPLiGEcAYOVNH%+#*H$kN+B9$>I-bt5b{7YEsgG>=3_ABeJQ1_DZTx zzX>Fj&68nQCJ$A$__hhi#lKV$5=M2|KAc%?>MtdYvOvh;NTY-;m$A&FIi>0kq?W}k zXK$^x^kD}u5IhnWEhw34tEhaF^-qUiE@ENq%?@C8v&cA=cMV?Gj5+j}yc=T`pRS== znnNeVONHF1V|n}z1owg=0N;N{Ik}z3dzax08uiMW*Iw_FA5Z{ny8jhYvoHOn${&hUbi6-J1cF0m1l5nAxcpmaZVOGDc?C*SFdB2k20HdcHMcksPqhvydgFOB!G&-Q#>H}FIQN&OIdhmDPM;Y- z(v|nXBmwjgr#lC7rv6-N2jIOUJLkZ{Z{gmJ1NqUnoFVhh8Pa^_G9x>|3bJU*DKZ3A zu>DoLb}vI>gMeASM{C&L-fB6A0Y7CuX@tjYb#oqQ^3q%)x7-|)I^j0c;B)h*WSvjI z=0O4AzW-!xcBgMS1p7PJ)c9) zA)c-J>gqfUg#j=w%R9r^Q?q6=)I|1{m)eUegNkKS8)nqGO$`r9gh14iPAjK2J*^Px z{dkw8@&HhnV1)_LA&LLx6Li?j%Yz~Jt?-v=5m2K6sR%2PEYA43#>)OWTEEIatOzzN z$0&KRrZ4wm%x=w`y!rirrT2q_lJ+0@4adcS#G#A_CGNDg62I;q`s}KVRlP&s^tz z&dfP;=FC0Ub2r=j6Y#C{^{nj2?-Irp&N24|L$h(KWh?nxD_f0qtRD96zDE+IGUMbL z(Fgs5YxA8iOY5OaBbHQi=O(11#mP?&k1$cBHvgB6qbtg2ogVz?28Tbse64|Qd7yia z6MZTUmiw8PWK`sWWG}Mfe*Z{F7;N>p{A7CCG5|_}IQ1{4+%fRLa6nhm7?3`$e4D-} zF5`uUC32LU{NkIAuCcT@IQ`fFDB&8C=(<}|1vQBKGgXOli?{gwle99Abez6rY@Uwql+JAL6 ziCgRAYcLZ(ULBkjos;Xnx-#Rjr@-m@QoS8O$a_g2pKOxW@*7j@BMubGsGpMF!4&yYG zfupdfdUp(7govM6JfXFkVT_y{$mbJ({6sXT7@Ou3stuO?e@ z(SwA@?RvPx=s#ZSj@hJRqnIqQdq(njF)8#l#Hoa(%vjLSy(H9C(wWjl>+4pzQqipX z6?K-!?UB-(Y^)*e;@D#SHl`DaE$r$VF~-OVn)t*{cdb_VJu%rCU1#SiyHWk<4oOeG z{5s@KF9K4|Sk<0UHewcYkkHt$D2yv+`;o(;lcTTlyvCxqpf)AyyBaA)&mkJkeSM4Fj(W%9I*V38x1&udvy`aKCg;WHwK;wjDl!dW zbw65!1gA|VGkw78fVAd(ngXvYwh0FYrouyu`b&qh_+GhxuwP7tq&zGbU^P=PyT_d= zIdO#-A1bcrY2h9pYEE6l$FvFJnpq*2%Dd#vgn!!(LKoeX#9|Lj^DJGV;m zQLZZKS-ki2(SEZ79g*XRT0$MLHdoSV7cPvM9vBEg`nzD$-5k^BB#jr$yXo^;%$cW` zOpehHA)W;HcG@)1CCARC(2`DO_z&7Xcr>7F`|SouB457_-c*aU*0KMADY>Kcx^8<1 zq&izV5BN@cV{={7Wu7Eb0@QBIh**)Uqv8L@=&DAAWdlM?NGC`KOsu!oqG|D95)_i}EGsJ&(KVc07E4_zVquX9R z2`_${C)F0{J{jey;Uc*tX#ZR}5bpaxw{;A}byeFKp)Tb;$@ez80@|oxgkUS-xo~42 z_(mTD!=qfXi=utKa!8O2%*|V)p256bmd4>O3s%R!-Yhg^@GV?Vd}u1%ixy~DiOR{q z`#F(T9A#+!vzb&I40q*?>I^Xx704%c=H9L+`dD4O4}YS7#9d`D?v<(1B1@UxFx znRO2S;1>nHt+(;>`}Bmq83=M+zs}O?cAClxnYgSXWVsp2WLoj{GeuXsWVuamku{{Z zgb4kK@*5qI7-g2|bR@(p9H@OYJ(C=Qmc}C$7UaSp_^h`%fU?#W^v<7*s0@V=x8EKt zUtOHr-z}eC9Mr0xrkxIpNQU7}>l1a&6Kjp(u8OS99@NUwL> z;!8@PJBON8PxHLUeZoJJfWZ91I6V{ZQh9Bmmy6C!?E_Ez& zqIye;A4sIdE|+J&JbLMhKcHsi`O3rf0!MXr;3ZWKkyZQFZ-l+L#Qa5N`~Erjs^29@ z{Cv66#dB{lCb88*TKkkiE{ABu9N%Z7Iib;B-qdgnQ;Ahp$%Sf2hF~{$%S2puqRBaB z)#}-tvXetI_J;(jfeg7RCLd$N9(QkohOd|PupHGJfv$lY+9{ftb`y)2od^>P^8_}W z4WGDjEYe`e{Ih@D)qM4^*_UZTn@AW3DjoMELp8_O?{D)^I?sG%I?a3J zS~}cTwP;>a0VOTmSOzOfBkb6yKAHvkvYRR@%RySCh}$JkI^ZED9;mZinqMc$6vP=R z>+n#zIE8T;Ht$QO0_42*OvpM1Z>szW=Mx2$vOo)PHzN8{r%XlHt5+vv zp-sxQc6?Fgh2t{4O8Dv(bFL9X?)Lek$Tlke;WM;O!W3uDY)6pm%fXmt%NyT<^y#9vB}g%EMMb-IQ?z1KHPlUerY37!+#|@B z!=$_t27`Imxh&l7BaNe5fPJX@R4oeSTR#m_uxNoAn~t(&=6gTJYRB0;^~pB=$%kJo zmw~&oTSyo>;4MK;Q>;!D0D6hL@s>Z4P5Mg7W-OU--iU9)_gvi{!c6c4 z?$oJG#hSS4WGkzK+B1VF+l&ZJp`?oZ34aaA>#DV|r$_e=-^HgP?;T%ZvKt6>laGe6 z>?dtK?$p|`vVz1%0MyLEBwk*>iv#9Fu5lObqTWla;Gf_tSO&>39usUs zu-X}33f){{0cjp?Mz(m;ttq;X>PpVM_K3VlPmrSK`_w%B3TxXRlwI;A%cq}-vX;`3 zkKD!vHejgFkE`BpHz9m})lmLvln3;|G)pGE;?X z8_9xa6K1qmS-K@!_H5|=gh~=#m51pY?K6~OO>0c}C)nl*S!O0WUb$Z67pkaweI*0~ zb9&L?+`>Dz(ed9)j*T)$DjDonY>o?x`mgBTj)!U9OA~~>g()h&>b`#5LM?2{84uw) zNu!DM!xsG7DbowB^Nsus($sy$1g)MUzTl@to+ zFka=|%67ulB_$^6I@x^dEE{g^S}1;7UVDu0R>-Bqr#$4C0^yOb{uJHsjvb)8@ip_8xi1{qKGnKmLi%2r78NoEuj6nzTp_QRb3e zzbE16VJL*@VoL|8v}DhWU<`|v*$7nWiEQ4!cmh?L7=zP7^Gk=!?M4sJ`~&Njeam9P zUl-sKlpne<6GSggvunjGZxbfkgIy$hbMd*SS!?qO#IP91HCT;QQHeZE@v)aBqJt)q zWjM-?0cR{sZG9aOTzb;O(vR8*ZD;y1{rbIFMNHSqBeGBAH8IG|MMh^<1fi1_%Ho&;K zWpnUNoXP)P8yP9&YdL$p=VB^R%7fr~{RGNUoTi}rjWb)@Eatp3@^K{bl6eOG%;r2g zu8pC%8kXZvvHvY7{$tV>(b_3nS>d$FWJr*Fjns!Bct}6tJZI-;1E9140_!80eKZBS z&(_RnZ=!A|l$jiY-@PJQF4>I?o`Wm&lf+VIBg~{F(#;zzt*V*@Ib1AJ#wvQT%1wnI z_!9(33mLe0erX$fupDj=yH5gJhq|?!ArtGPoh2>|@9nxRJSPzt8rA7$$U@^Eo|f+2 zDXCmVUWbve`cX#RV{`s2RrmZ8YE;4tYc2=)8Z2j%YJHMJ0X0P1wppwhFvpphgSQts zNw=bq!<$dhw?|2I7%7KoAA{a3*^L!OlBfe=EvzWtGAJBtMP z)Q?va5OrD$Cv`|kYF}}#98!|0#eGeh6z8FmxnB{L9xVgD=JPc&;+!z!wZL(Q)s(LS z>)N*;@~!~@cfHp>TdNm{m$n^s!>92;F1z&-fp*sx4&h5obk9IIn&UkX$$@TVrqLxx z9=->>+U`XA55c+yfUmRpFN3{*zC?5QHtV?)hgQWdnW4Ce)+lZV+z0Dqb0Zb|b+WWoV&Yx3> zJuX^KM*Hl{RkHsn|72*-KuS!`e`{vukeJeG5)L}N{*>KHI_Es-8haCkBT-O%VSp@D zIHtOR_~ppR@NYyK+KKh!^sCjgQe;6lkL{-=KHjUz8(ycZb-T5<0YH}Y{Z&*nCeD9a zT>{8BD0&C59LER#(>o6hjp~;EGZYDcF)0c_Hl70fr}^05&>Ha_6mkpw*#hu4 + /// 验证码 + /// + 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 }); } } }