From 0f687a7e34cff874d7ba0c77d790082c5f1e7c60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A9=99=E5=AD=90?= <454313500@qq.com> Date: Sat, 19 Oct 2024 16:17:26 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=8C=E6=88=90=E5=BE=AE=E4=BF=A1?= =?UTF-8?q?=E5=B0=8F=E7=A8=8B=E5=BA=8F=E8=B4=A6=E6=88=B7=E5=BA=94=E7=94=A8?= =?UTF-8?q?=E6=9C=8D=E5=8A=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Yi.Framework.WeChat.MiniProgram.csproj | 8 ++ .../YiFrameworkWeChatMiniProgramModule.cs | 19 +++ .../Dtos/Account/BindInput.cs | 8 ++ .../Dtos/Account/LoginInput.cs | 4 +- .../Dtos/Account/RegisterInput.cs | 43 ++++++ .../Account/CollectiblesAccountService.cs | 41 ------ .../WeChatMiniProgramAccountService.cs | 123 ++++++++++++++++++ .../Consts/AuthTypeConst.cs | 6 + ...ramework.DigitalCollectibles.Domain.csproj | 2 + ...rameworkDigitalCollectiblesDomainModule.cs | 4 +- .../Account/AuthCreateOrUpdateInputDto.cs | 12 ++ .../Dtos/Account/LoginOutputDto.cs | 7 + .../IServices/IAccountService.cs | 24 +++- .../IServices/IAuthService.cs | 10 ++ .../Services/AccountService.cs | 68 +++++++--- .../Services/Authentication/AuthService.cs | 57 ++++++-- 16 files changed, 363 insertions(+), 73 deletions(-) create mode 100644 Yi.Abp.Net8/framework/Yi.Framework.WeChat.MiniProgram/Yi.Framework.WeChat.MiniProgram.csproj create mode 100644 Yi.Abp.Net8/framework/Yi.Framework.WeChat.MiniProgram/YiFrameworkWeChatMiniProgramModule.cs create mode 100644 Yi.Abp.Net8/module/digital-collectibles/Yi.Framework.DigitalCollectibles.Application.Contracts/Dtos/Account/BindInput.cs create mode 100644 Yi.Abp.Net8/module/digital-collectibles/Yi.Framework.DigitalCollectibles.Application.Contracts/Dtos/Account/RegisterInput.cs delete mode 100644 Yi.Abp.Net8/module/digital-collectibles/Yi.Framework.DigitalCollectibles.Application/Services/Account/CollectiblesAccountService.cs create mode 100644 Yi.Abp.Net8/module/digital-collectibles/Yi.Framework.DigitalCollectibles.Application/Services/Account/WeChatMiniProgramAccountService.cs create mode 100644 Yi.Abp.Net8/module/digital-collectibles/Yi.Framework.DigitalCollectibles.Domain.Shared/Consts/AuthTypeConst.cs create mode 100644 Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Application.Contracts/Dtos/Account/AuthCreateOrUpdateInputDto.cs create mode 100644 Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Application.Contracts/Dtos/Account/LoginOutputDto.cs create mode 100644 Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Application.Contracts/IServices/IAuthService.cs diff --git a/Yi.Abp.Net8/framework/Yi.Framework.WeChat.MiniProgram/Yi.Framework.WeChat.MiniProgram.csproj b/Yi.Abp.Net8/framework/Yi.Framework.WeChat.MiniProgram/Yi.Framework.WeChat.MiniProgram.csproj new file mode 100644 index 00000000..4513d436 --- /dev/null +++ b/Yi.Abp.Net8/framework/Yi.Framework.WeChat.MiniProgram/Yi.Framework.WeChat.MiniProgram.csproj @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/Yi.Abp.Net8/framework/Yi.Framework.WeChat.MiniProgram/YiFrameworkWeChatMiniProgramModule.cs b/Yi.Abp.Net8/framework/Yi.Framework.WeChat.MiniProgram/YiFrameworkWeChatMiniProgramModule.cs new file mode 100644 index 00000000..029b29ed --- /dev/null +++ b/Yi.Abp.Net8/framework/Yi.Framework.WeChat.MiniProgram/YiFrameworkWeChatMiniProgramModule.cs @@ -0,0 +1,19 @@ +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.Caching; +using Yi.Framework.Core; +using Yi.Framework.WeChat.MiniProgram.Token; + +namespace Yi.Framework.WeChat.MiniProgram; + +[DependsOn(typeof(YiFrameworkCoreModule), + typeof(AbpCachingModule))] +public class YiFrameworkWeChatMiniProgramModule: AbpModule +{ + public override void ConfigureServices(ServiceConfigurationContext context) + { + var services = context.Services; + var configuration = context.Services.GetConfiguration(); + Configure(configuration.GetSection("WeChatMiniProgram")); + services.AddSingleton(); + } +} \ No newline at end of file diff --git a/Yi.Abp.Net8/module/digital-collectibles/Yi.Framework.DigitalCollectibles.Application.Contracts/Dtos/Account/BindInput.cs b/Yi.Abp.Net8/module/digital-collectibles/Yi.Framework.DigitalCollectibles.Application.Contracts/Dtos/Account/BindInput.cs new file mode 100644 index 00000000..d821ebec --- /dev/null +++ b/Yi.Abp.Net8/module/digital-collectibles/Yi.Framework.DigitalCollectibles.Application.Contracts/Dtos/Account/BindInput.cs @@ -0,0 +1,8 @@ +namespace Yi.Framework.DigitalCollectibles.Application.Contracts.Dtos.Account; + +public class BindInput +{ + public string JsCode { get; set; } + + public long Phone { get; set; } +} \ No newline at end of file diff --git a/Yi.Abp.Net8/module/digital-collectibles/Yi.Framework.DigitalCollectibles.Application.Contracts/Dtos/Account/LoginInput.cs b/Yi.Abp.Net8/module/digital-collectibles/Yi.Framework.DigitalCollectibles.Application.Contracts/Dtos/Account/LoginInput.cs index e4a419a2..58cc40a0 100644 --- a/Yi.Abp.Net8/module/digital-collectibles/Yi.Framework.DigitalCollectibles.Application.Contracts/Dtos/Account/LoginInput.cs +++ b/Yi.Abp.Net8/module/digital-collectibles/Yi.Framework.DigitalCollectibles.Application.Contracts/Dtos/Account/LoginInput.cs @@ -3,7 +3,7 @@ public class LoginInput { /// - /// 微信小程序code + /// 微信小程序jscode /// - public string Code { get; set; } + public string JsCode { get; set; } } \ No newline at end of file diff --git a/Yi.Abp.Net8/module/digital-collectibles/Yi.Framework.DigitalCollectibles.Application.Contracts/Dtos/Account/RegisterInput.cs b/Yi.Abp.Net8/module/digital-collectibles/Yi.Framework.DigitalCollectibles.Application.Contracts/Dtos/Account/RegisterInput.cs new file mode 100644 index 00000000..6e4db2e7 --- /dev/null +++ b/Yi.Abp.Net8/module/digital-collectibles/Yi.Framework.DigitalCollectibles.Application.Contracts/Dtos/Account/RegisterInput.cs @@ -0,0 +1,43 @@ +namespace Yi.Framework.DigitalCollectibles.Application.Contracts.Dtos.Account; + +public class RegisterInput +{ + + //电话号码,根据code的表示来获取 + + /// + /// 账号 + /// + public string UserName { get; set; } + + /// + /// 密码 + /// + public string Password { get; set; } + + /// + /// 唯一标识码 + /// + public string? Uuid { get; set; } + + /// + /// 电话 + /// + public long Phone { get; set; } + + /// + /// 验证码 + /// + public string? Code { get; set; } + + /// + /// 昵称 + /// + public string? Nick{ get; set; } + + + /// + /// 微信小程序code + /// + public string JsCode { get; set; } +} \ No newline at end of file diff --git a/Yi.Abp.Net8/module/digital-collectibles/Yi.Framework.DigitalCollectibles.Application/Services/Account/CollectiblesAccountService.cs b/Yi.Abp.Net8/module/digital-collectibles/Yi.Framework.DigitalCollectibles.Application/Services/Account/CollectiblesAccountService.cs deleted file mode 100644 index 18bbc73b..00000000 --- a/Yi.Abp.Net8/module/digital-collectibles/Yi.Framework.DigitalCollectibles.Application/Services/Account/CollectiblesAccountService.cs +++ /dev/null @@ -1,41 +0,0 @@ -using Volo.Abp.Application.Services; - -namespace Yi.Framework.DigitalCollectibles.Application.Services.Account; - -public class CollectiblesAccountService: ApplicationService -{ - /// - /// 小程序登录 - /// - /// - public Task PostLoginAsync() - { - throw new NotImplementedException(); - //根据code去获取wxid - //判断wxid中是否有对应的userid关系 - //果然有,直接根据userid返回该用户token - //如果没有,返回结果即可 - } - - - /// - /// 小程序绑定账号 - /// - /// - public Task PostBindAsync() - { - throw new NotImplementedException(); - //根据code去获取wxid - //校验手机号 - //根据手机号查询用户信息 - //将wxid和用户user绑定 - } - - //小程序注册 - public Task PostRegisterAsync() - { - throw new NotImplementedException(); - //走普通注册流程 - //同时再加一个小程序绑定即可 - } -} \ No newline at end of file diff --git a/Yi.Abp.Net8/module/digital-collectibles/Yi.Framework.DigitalCollectibles.Application/Services/Account/WeChatMiniProgramAccountService.cs b/Yi.Abp.Net8/module/digital-collectibles/Yi.Framework.DigitalCollectibles.Application/Services/Account/WeChatMiniProgramAccountService.cs new file mode 100644 index 00000000..e309e958 --- /dev/null +++ b/Yi.Abp.Net8/module/digital-collectibles/Yi.Framework.DigitalCollectibles.Application/Services/Account/WeChatMiniProgramAccountService.cs @@ -0,0 +1,123 @@ +using Microsoft.AspNetCore.Mvc; +using Volo.Abp.Application.Services; +using Yi.Framework.DigitalCollectibles.Application.Contracts.Dtos.Account; +using Yi.Framework.DigitalCollectibles.Domain.Shared.Consts; +using Yi.Framework.DigitalCollectibles.Domain.Shared.Enums; +using Yi.Framework.Rbac.Application.Contracts.Dtos.Account; +using Yi.Framework.Rbac.Application.Contracts.IServices; +using Yi.Framework.Rbac.Domain.Shared.Enums; +using Yi.Framework.WeChat.MiniProgram; +using Yi.Framework.WeChat.MiniProgram.HttpModels; + +namespace Yi.Framework.DigitalCollectibles.Application.Services.Account; + +/// +/// 微信小程序账户应用服务 +/// +public class WeChatMiniProgramAccountService : ApplicationService +{ + private readonly IWeChatMiniProgramManager _weChatMiniProgramManager; + private readonly IAuthService _authService; + private readonly IAccountService _accountService; + + public WeChatMiniProgramAccountService(IWeChatMiniProgramManager weChatMiniProgramManager, IAuthService authService, + IAccountService accountService) + { + _weChatMiniProgramManager = weChatMiniProgramManager; + _authService = authService; + _accountService = accountService; + } + + /// + /// 使用小程序jsCode登录意社区账号 + /// + /// + [HttpPost("wechat/mini-program/account/login")] + public async Task PostLoginAsync(LoginInput intput) + { + var output = new LoginOutput(); + //根据code去获取wxid + //判断wxid中是否有对应的userid关系 + //果然有,直接根据userid返回该用户token + //如果没有,返回结果即可 + var openId = (await _weChatMiniProgramManager.Code2SessionAsync(new Code2SessionInput(intput.JsCode))).openid; + + var authInfo = await _authService.TryGetByOpenIdAsync(openId, AuthTypeConst.WeChatMiniProgram); + if (authInfo is null) + { + output.Result = LoginResultEnum.Error; + } + + //根据用户id获取到用户信息 + var result = await _accountService.PostLoginAsync(authInfo.UserId); + output.Result = LoginResultEnum.Success; + output.Token = result.Token; + + return output; + } + + + /// + /// 将小程序第三方授权绑定给意社区账号 + /// + /// + /// + [HttpPost("wechat/mini-program/account/bind")] + public async Task PostBindAsync(BindInput input) + { + //校验手机号与验证码 + //根据手机号查询用户信息 + //根据code去获取wxid + //将wxid和用户user绑定 + var userInfo = await _accountService.GetAsync(null, input.Phone); + if (userInfo is null) + { + throw new UserFriendlyException("该手机号未被注册,无法绑定微信小程序"); + } + + //验证手机号的验证码 + await _accountService.PostCaptchaPhoneAsync(ValidationPhoneTypeEnum.Register, new PhoneCaptchaImageDto + { + Phone = input.Phone.ToString() + }); + + var openId = (await _weChatMiniProgramManager.Code2SessionAsync(new Code2SessionInput(input.JsCode))).openid; + + + await PostBindToAuthAsync(userInfo.User.Id, openId, userInfo.User.UserName); + } + + private async Task PostBindToAuthAsync(Guid userId, string openId, string? name = null) + { + await _authService.CreateAsync(new AuthCreateOrUpdateInputDto + { + UserId = userId, + OpenId = openId, + Name = name ?? "未知", + AuthType = AuthTypeConst.WeChatMiniProgram + }); + } + + /// + /// 使用小程序去注册意社区账号 + /// + /// + [HttpPost("wechat/mini-program/account/register")] + public async Task PostRegisterAsync(RegisterInput input) + { + //走普通注册流程 + //同时再加一个小程序绑定即可 + await _accountService.PostRegisterAsync(new RegisterDto + { + UserName = input.UserName, + Password = input.Password, + Uuid = input.Uuid, + Phone = input.Phone, + Code = input.Code, + Nick = input.Nick + }); + var userInfo = await _accountService.GetAsync(input.UserName, null); + var openId = (await _weChatMiniProgramManager.Code2SessionAsync(new Code2SessionInput(input.JsCode))).openid; + await PostBindToAuthAsync(userInfo.User.Id, openId, userInfo.User.UserName); + } +} \ No newline at end of file diff --git a/Yi.Abp.Net8/module/digital-collectibles/Yi.Framework.DigitalCollectibles.Domain.Shared/Consts/AuthTypeConst.cs b/Yi.Abp.Net8/module/digital-collectibles/Yi.Framework.DigitalCollectibles.Domain.Shared/Consts/AuthTypeConst.cs new file mode 100644 index 00000000..b3d1e1ea --- /dev/null +++ b/Yi.Abp.Net8/module/digital-collectibles/Yi.Framework.DigitalCollectibles.Domain.Shared/Consts/AuthTypeConst.cs @@ -0,0 +1,6 @@ +namespace Yi.Framework.DigitalCollectibles.Domain.Shared.Consts; + +public class AuthTypeConst +{ + public const string WeChatMiniProgram = "WeChatMiniProgram"; +} \ No newline at end of file diff --git a/Yi.Abp.Net8/module/digital-collectibles/Yi.Framework.DigitalCollectibles.Domain/Yi.Framework.DigitalCollectibles.Domain.csproj b/Yi.Abp.Net8/module/digital-collectibles/Yi.Framework.DigitalCollectibles.Domain/Yi.Framework.DigitalCollectibles.Domain.csproj index 431565d5..de97e8c8 100644 --- a/Yi.Abp.Net8/module/digital-collectibles/Yi.Framework.DigitalCollectibles.Domain/Yi.Framework.DigitalCollectibles.Domain.csproj +++ b/Yi.Abp.Net8/module/digital-collectibles/Yi.Framework.DigitalCollectibles.Domain/Yi.Framework.DigitalCollectibles.Domain.csproj @@ -10,10 +10,12 @@ + + diff --git a/Yi.Abp.Net8/module/digital-collectibles/Yi.Framework.DigitalCollectibles.Domain/YiFrameworkDigitalCollectiblesDomainModule.cs b/Yi.Abp.Net8/module/digital-collectibles/Yi.Framework.DigitalCollectibles.Domain/YiFrameworkDigitalCollectiblesDomainModule.cs index 9171ca5b..4a0a0f59 100644 --- a/Yi.Abp.Net8/module/digital-collectibles/Yi.Framework.DigitalCollectibles.Domain/YiFrameworkDigitalCollectiblesDomainModule.cs +++ b/Yi.Abp.Net8/module/digital-collectibles/Yi.Framework.DigitalCollectibles.Domain/YiFrameworkDigitalCollectiblesDomainModule.cs @@ -4,12 +4,14 @@ using Volo.Abp.Modularity; using Yi.Framework.DigitalCollectibles.Domain.Shared; using Yi.Framework.Mapster; using Yi.Framework.SettingManagement.Domain; +using Yi.Framework.WeChat.MiniProgram; namespace Yi.Framework.DigitalCollectibles.Domain { [DependsOn( typeof(YiFrameworkDigitalCollectiblesDomainSharedModule), - + + typeof(YiFrameworkWeChatMiniProgramModule), typeof(YiFrameworkSettingManagementDomainModule), typeof(YiFrameworkMapsterModule), typeof(AbpDddDomainModule), diff --git a/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Application.Contracts/Dtos/Account/AuthCreateOrUpdateInputDto.cs b/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Application.Contracts/Dtos/Account/AuthCreateOrUpdateInputDto.cs new file mode 100644 index 00000000..84f9a22a --- /dev/null +++ b/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Application.Contracts/Dtos/Account/AuthCreateOrUpdateInputDto.cs @@ -0,0 +1,12 @@ +namespace Yi.Framework.Rbac.Application.Contracts.Dtos.Account; + +public class AuthCreateOrUpdateInputDto +{ + public Guid UserId { get; set; } + + public string OpenId { get; set; } + + public string Name { get; set; } + + public string AuthType { get; set; } +} \ No newline at end of file diff --git a/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Application.Contracts/Dtos/Account/LoginOutputDto.cs b/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Application.Contracts/Dtos/Account/LoginOutputDto.cs new file mode 100644 index 00000000..d5958903 --- /dev/null +++ b/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Application.Contracts/Dtos/Account/LoginOutputDto.cs @@ -0,0 +1,7 @@ +namespace Yi.Framework.Rbac.Application.Contracts.Dtos.Account; + +public class LoginOutputDto +{ + public string Token { get; set; } + public string RefreshToken { get; set; } +} \ No newline at end of file diff --git a/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Application.Contracts/IServices/IAccountService.cs b/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Application.Contracts/IServices/IAccountService.cs index 33263b8e..390e07de 100644 --- a/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Application.Contracts/IServices/IAccountService.cs +++ b/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Application.Contracts/IServices/IAccountService.cs @@ -1,6 +1,7 @@ using Volo.Abp.Application.Services; using Yi.Framework.Rbac.Application.Contracts.Dtos.Account; using Yi.Framework.Rbac.Domain.Shared.Dtos; +using Yi.Framework.Rbac.Domain.Shared.Enums; namespace Yi.Framework.Rbac.Application.Contracts.IServices { @@ -8,8 +9,29 @@ namespace Yi.Framework.Rbac.Application.Contracts.IServices { Task GetAsync(); Task GetCaptchaImageAsync(); - Task PostLoginAsync(LoginInputVo input); + Task PostLoginAsync(LoginInputVo input); Task PostRegisterAsync(RegisterDto input); Task RestPasswordAsync(Guid userId, RestPasswordDto input); + + /// + /// 提供其他服务使用,根据用户id,直接返回token + /// + /// + Task PostLoginAsync(Guid userId); + + /// + /// 根据信息查询用户,可能为空,代表该用户不存在或禁用 + /// + /// + /// + /// + Task GetAsync(string? userName,long? phone); + + /// + /// 手机验证码 + /// + /// + Task PostCaptchaPhoneAsync(ValidationPhoneTypeEnum validationPhoneType, + PhoneCaptchaImageDto input); } } diff --git a/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Application.Contracts/IServices/IAuthService.cs b/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Application.Contracts/IServices/IAuthService.cs new file mode 100644 index 00000000..72239f33 --- /dev/null +++ b/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Application.Contracts/IServices/IAuthService.cs @@ -0,0 +1,10 @@ +using Volo.Abp.Application.Dtos; +using Yi.Framework.Rbac.Application.Contracts.Dtos.Account; + +namespace Yi.Framework.Rbac.Application.Contracts.IServices; + +public interface IAuthService +{ + Task TryGetByOpenIdAsync(string openId, string authType); + Task CreateAsync(AuthCreateOrUpdateInputDto input); +} \ No newline at end of file diff --git a/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Application/Services/AccountService.cs b/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Application/Services/AccountService.cs index 2830a9fe..50e59507 100644 --- a/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Application/Services/AccountService.cs +++ b/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Application/Services/AccountService.cs @@ -101,7 +101,7 @@ namespace Yi.Framework.Rbac.Application.Services /// /// [AllowAnonymous] - public async Task PostLoginAsync(LoginInputVo input) + public async Task PostLoginAsync(LoginInputVo input) { if (string.IsNullOrEmpty(input.Password) || string.IsNullOrEmpty(input.UserName)) { @@ -115,10 +115,21 @@ namespace Yi.Framework.Rbac.Application.Services //校验 await _accountManager.LoginValidationAsync(input.UserName, input.Password, x => user = x); + return await PostLoginAsync(user.Id); + } + + + /// + /// 提供其他服务使用,根据用户id,直接返回token + /// + /// + [RemoteService(isEnabled: false)] + public async Task PostLoginAsync(Guid userId) + { var userInfo = new UserRoleMenuDto(); //获取token - var accessToken = await _accountManager.GetTokenByUserIdAsync(user.Id, (info) => userInfo = info); - var refreshToken = _accountManager.CreateRefreshToken(user.Id); + var accessToken = await _accountManager.GetTokenByUserIdAsync(userId, (info) => userInfo = info); + var refreshToken = _accountManager.CreateRefreshToken(userId); //这里抛出一个登录的事件,也可以在全部流程走完,在应用层组装 if (_httpContextAccessor.HttpContext is not null) @@ -130,10 +141,9 @@ namespace Yi.Framework.Rbac.Application.Services await LocalEventBus.PublishAsync(loginEto); } - return new { Token = accessToken, RefreshToken = refreshToken }; + return new LoginOutputDto { Token = accessToken, RefreshToken = refreshToken }; } - /// /// 刷新token /// @@ -158,7 +168,7 @@ namespace Yi.Framework.Rbac.Application.Services var uuid = _guidGenerator.Create(); var captcha = _captcha.Generate(uuid.ToString()); var enableCaptcha = _rbacOptions.EnableCaptcha; - return new CaptchaImageDto { Img = captcha.Bytes, Uuid = uuid,IsEnableCaptcha= enableCaptcha }; + return new CaptchaImageDto { Img = captcha.Bytes, Uuid = uuid, IsEnableCaptcha = enableCaptcha }; } /// @@ -202,17 +212,19 @@ namespace Yi.Framework.Rbac.Application.Services /// 手机验证码 /// /// - private async Task PostCaptchaPhoneAsync(ValidationPhoneTypeEnum validationPhoneType, + public async Task PostCaptchaPhoneAsync(ValidationPhoneTypeEnum validationPhoneType, PhoneCaptchaImageDto input) { await ValidationPhone(input.Phone); - + //注册的手机号验证,是不能已经注册过的 - if (validationPhoneType == ValidationPhoneTypeEnum.Register&& await _userRepository.IsAnyAsync(x => x.Phone.ToString() == input.Phone)) - { - throw new UserFriendlyException("该手机号已被注册!"); - } - + //这里为了统一,绑定流程,不在次校验,而是在注册时校验 + // if (validationPhoneType == ValidationPhoneTypeEnum.Register && + // await _userRepository.IsAnyAsync(x => x.Phone.ToString() == input.Phone)) + // { + // throw new UserFriendlyException("该手机号已被注册!"); + // } + var value = await _phoneCache.GetAsync(new CaptchaPhoneCacheKey(validationPhoneType, input.Phone)); //防止暴刷 @@ -264,7 +276,7 @@ namespace Yi.Framework.Rbac.Application.Services { //校验验证码,根据电话号码获取 value,比对验证码已经uuid await ValidationPhoneCaptchaAsync(ValidationPhoneTypeEnum.RetrievePassword, input.Phone, input.Code); - + var entity = await _userRepository.GetFirstAsync(x => x.Phone == input.Phone); if (entity is null) { @@ -272,7 +284,7 @@ namespace Yi.Framework.Rbac.Application.Services } await _accountManager.RestPasswordAsync(entity.Id, input.Password); - + return entity.UserName; } @@ -322,6 +334,30 @@ namespace Yi.Framework.Rbac.Application.Services return output; } + [RemoteService(isEnabled: false)] + public async Task GetAsync(string? userName, long? phone) + { + var user = await _userRepository._DbQueryable + .WhereIF(userName is not null, x => x.UserName == userName) + .WhereIF(phone is not null, x => x.Phone == phone) + .Where(x => x.State == true) + .FirstAsync(); + + //该用户不存在 + if (user is null) + { + return null; + } + + //注意用户名大小写数据库不敏感问题 + if (userName is not null && user.UserName.Equals(userName)) + { + throw new UserFriendlyException($"该用户名不存在或已禁用-{userName}"); + } + + var output = await _userManager.GetInfoAsync(user.Id); + return output; + } /// /// 获取当前登录用户的前端路由 @@ -430,7 +466,7 @@ namespace Yi.Framework.Rbac.Application.Services /// public async Task UpdateIconAsync(UpdateIconDto input) { - Guid userId=input.UserId == null?_currentUser.GetId():input.UserId.Value; + Guid userId = input.UserId == null ? _currentUser.GetId() : input.UserId.Value; var entity = await _userRepository.GetByIdAsync(userId); diff --git a/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Application/Services/Authentication/AuthService.cs b/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Application/Services/Authentication/AuthService.cs index ecdbf68c..3ff2d894 100644 --- a/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Application/Services/Authentication/AuthService.cs +++ b/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Application/Services/Authentication/AuthService.cs @@ -7,8 +7,10 @@ using SqlSugar; using Volo.Abp; using Volo.Abp.Application.Dtos; using Volo.Abp.Domain.Repositories; +using Volo.Abp.Users; using Yi.Framework.Ddd.Application; using Yi.Framework.Rbac.Application.Contracts.Dtos.Account; +using Yi.Framework.Rbac.Application.Contracts.IServices; using Yi.Framework.Rbac.Domain.Authorization; using Yi.Framework.Rbac.Domain.Managers; using Yi.Framework.SqlSugarCore.Abstractions; @@ -18,13 +20,17 @@ namespace Yi.Framework.Rbac.Application.Services.Authentication /// /// 第三方授权服务 /// - public class AuthService : YiCrudAppService + public class AuthService : + YiCrudAppService, + IAuthService { private HttpContext HttpContext { get; set; } private ILogger _logger; private ISqlSugarRepository _repository; private IAccountManager _accountManager; - public AuthService(IAccountManager accountManager, IHttpContextAccessor httpContextAccessor, ILogger logger, ISqlSugarRepository repository) : base(repository) + + public AuthService(IAccountManager accountManager, IHttpContextAccessor httpContextAccessor, + ILogger logger, ISqlSugarRepository repository) : base(repository) { _logger = logger; HttpContext = httpContextAccessor.HttpContext ?? throw new ApplicationException("未注册Http"); @@ -49,8 +55,9 @@ namespace Yi.Framework.Rbac.Application.Services.Authentication { throw new UserFriendlyException("第三方登录失败,请先注册后,在个人中心进行绑定该第三方后使用"); } + var accessToken = await _accountManager.GetTokenByUserIdAsync(authEntity.UserId); - return new { token= accessToken }; + return new { token = accessToken }; } /// @@ -71,6 +78,7 @@ namespace Yi.Framework.Rbac.Application.Services.Authentication { throw new UserFriendlyException("绑定失败,该第三方账号已被注册"); } + var authAggregateRoot = new AuthAggregateRoot(scheme, userId ?? Guid.Empty, openId, name); await _repository.InsertAsync(authAggregateRoot); @@ -84,6 +92,7 @@ namespace Yi.Framework.Rbac.Application.Services.Authentication { throw new UserFriendlyException(authenticateResult.Failure.Message); } + var openidClaim = authenticateResult.Principal.Claims.Where(x => x.Type == "urn:openid").FirstOrDefault(); var nameClaim = authenticateResult.Principal.Claims.Where(x => x.Type == "urn:name").FirstOrDefault(); return (openidClaim.Value, nameClaim.Value); @@ -104,21 +113,31 @@ namespace Yi.Framework.Rbac.Application.Services.Authentication return (await GetListAsync(input)).Items; } + public async Task TryGetByOpenIdAsync(string openId, string authType) + { + var entity = await _repository._DbQueryable.Where(x => x.OpenId == openId) + .Where(x => x.AuthType == authType) + .FirstAsync(); + var output = await MapToGetOutputDtoAsync(entity); + return output; + } public override async Task> GetListAsync(AuthGetListInput input) { RefAsync total = 0; - var entities = await _repository._DbQueryable.WhereIF(input.UserId is not null, x => x.UserId == input.UserId) - .WhereIF(!string.IsNullOrEmpty(input.AuthType), x => x.AuthType == input.AuthType) - .WhereIF(!string.IsNullOrEmpty(input.OpenId), x => x.OpenId == input.OpenId) - .WhereIF(input.StartTime is not null && input.EndTime is not null, x => x.CreationTime >= input.StartTime && x.CreationTime <= input.EndTime) - .ToPageListAsync(input.SkipCount, input.MaxResultCount, total); + var entities = await _repository._DbQueryable + .WhereIF(input.UserId is not null, x => x.UserId == input.UserId) + .WhereIF(!string.IsNullOrEmpty(input.AuthType), x => x.AuthType == input.AuthType) + .WhereIF(!string.IsNullOrEmpty(input.OpenId), x => x.OpenId == input.OpenId) + .WhereIF(input.StartTime is not null && input.EndTime is not null, + x => x.CreationTime >= input.StartTime && x.CreationTime <= input.EndTime) + .ToPageListAsync(input.SkipCount, input.MaxResultCount, total); return new PagedResultDto(total, await MapToGetListOutputDtosAsync(entities)); } [RemoteService(IsEnabled = false)] - public override Task UpdateAsync(Guid id, AuthOutputDto input) + public override Task UpdateAsync(Guid id, AuthCreateOrUpdateInputDto input) { throw new NotImplementedException(); } @@ -135,9 +154,23 @@ namespace Yi.Framework.Rbac.Application.Services.Authentication } [RemoteService(IsEnabled = false)] - public override Task CreateAsync(AuthOutputDto input) + public override async Task CreateAsync(AuthCreateOrUpdateInputDto input) { - throw new NotImplementedException(); + var entity = await MapToEntityAsync(input); + await _repository.InsertAsync(entity); + return MapToGetOutputDto(entity); + } + + protected override async Task CheckCreateInputDtoAsync(AuthCreateOrUpdateInputDto input) + { + //同一个类型,一个用户只能绑定一个第三方授权 + var isAny = await _repository._DbQueryable.Where(x => x.AuthType == input.AuthType) + .Where(x => x.OpenId == input.OpenId || x.UserId == input.UserId).AnyAsync(); + + if (isAny) + { + throw new UserFriendlyException("该用户已有此应用授权"); + } } } -} +} \ No newline at end of file