From 5649da83bcd615ece3d504630d845ff6b78bb849 Mon Sep 17 00:00:00 2001 From: colin Date: Mon, 25 Aug 2025 07:49:45 +0800 Subject: [PATCH] feat(account): add QR code login --- .../Account/Localization/Resources/en.json | 3 + .../Localization/Resources/zh-Hans.json | 3 + .../Pages/Account/IdentityServerLoginModel.cs | 22 +-- .../Pages/Account/OpenIddictLoginModel.cs | 6 +- .../AbpAccountWebModule.cs | 4 +- .../Pages/Account/Login.cshtml | 21 ++- .../Pages/Account/Login.cshtml.cs | 151 ++++++++++++++++-- .../Pages/Account/Login.js | 108 +++++++++++-- .../wwwroot/client-proxies/qrcode-proxy.js | 33 ++++ .../Localization/Resources/zh-Hans.json | 2 +- 10 files changed, 306 insertions(+), 47 deletions(-) create mode 100644 aspnet-core/modules/account/LINGYUN.Abp.Account.Web/wwwroot/client-proxies/qrcode-proxy.js diff --git a/aspnet-core/modules/account/LINGYUN.Abp.Account.Application.Contracts/LINGYUN/Abp/Account/Localization/Resources/en.json b/aspnet-core/modules/account/LINGYUN.Abp.Account.Application.Contracts/LINGYUN/Abp/Account/Localization/Resources/en.json index b0f6e7cbc..38ad3e9b9 100644 --- a/aspnet-core/modules/account/LINGYUN.Abp.Account.Application.Contracts/LINGYUN/Abp/Account/Localization/Resources/en.json +++ b/aspnet-core/modules/account/LINGYUN.Abp.Account.Application.Contracts/LINGYUN/Abp/Account/Localization/Resources/en.json @@ -15,6 +15,8 @@ "DisplayName:EmailVerifyCode": "Mail verification code", "DisplayName:WeChatCode": "Wechat login code", "DisplayName:AuthenticatorCode": "Authenticator Code", + "DisplayName:PhoneNumber": "Phone Number", + "DisplayName:Code": "Code", "TwoFactor": "Two factor authentication", "TwoFactor:Enabled": "TwoFactor Enabled", "TwoFactor:Email": "Email", @@ -54,6 +56,7 @@ "SecurityLogs": "Security Logs", "PasswordLogin": "Login with password", "PhoneNumberLogin": "Login with phone", + "ScanQrCodeLogin": "Login with scan", "DisplayName:Abp.Account.EnablePhoneNumberLogin": "Authenticate with a phone number", "Description:Abp.Account.EnablePhoneNumberLogin": "Indicates whether the server allows users to use mobile phone verification codes." } diff --git a/aspnet-core/modules/account/LINGYUN.Abp.Account.Application.Contracts/LINGYUN/Abp/Account/Localization/Resources/zh-Hans.json b/aspnet-core/modules/account/LINGYUN.Abp.Account.Application.Contracts/LINGYUN/Abp/Account/Localization/Resources/zh-Hans.json index 527f80541..896e15146 100644 --- a/aspnet-core/modules/account/LINGYUN.Abp.Account.Application.Contracts/LINGYUN/Abp/Account/Localization/Resources/zh-Hans.json +++ b/aspnet-core/modules/account/LINGYUN.Abp.Account.Application.Contracts/LINGYUN/Abp/Account/Localization/Resources/zh-Hans.json @@ -15,6 +15,8 @@ "DisplayName:EmailVerifyCode": "邮件验证码", "DisplayName:WeChatCode": "微信登录凭证", "DisplayName:AuthenticatorCode": "验证代码", + "DisplayName:PhoneNumber": "手机号码", + "DisplayName:Code": "验证码", "TwoFactor": "双因素身份验证", "TwoFactor:Enabled": "启用双因素认证", "TwoFactor:Email": "邮箱验证", @@ -54,6 +56,7 @@ "SecurityLogs": "安全日志", "PasswordLogin": "密码登录", "PhoneNumberLogin": "验证码登录", + "ScanQrCodeLogin": "扫码登录", "DisplayName:Abp.Account.EnablePhoneNumberLogin": "使用手机验证码进行身份验证", "Description:Abp.Account.EnablePhoneNumberLogin": "表示服务器是否允许用户使用手机验证码进行身份验证。" } diff --git a/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.IdentityServer/Pages/Account/IdentityServerLoginModel.cs b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.IdentityServer/Pages/Account/IdentityServerLoginModel.cs index f62229941..c54a5e42e 100644 --- a/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.IdentityServer/Pages/Account/IdentityServerLoginModel.cs +++ b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.IdentityServer/Pages/Account/IdentityServerLoginModel.cs @@ -4,6 +4,7 @@ using IdentityServer4.Services; using IdentityServer4.Stores; using LINGYUN.Abp.Account.Web.ExternalProviders; using LINGYUN.Abp.Account.Web.Models; +using LINGYUN.Abp.Account.Web.Pages.Account; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Options; @@ -18,7 +19,6 @@ using Volo.Abp.Identity; using Volo.Abp.IdentityServer.AspNetIdentity; using Volo.Abp.MultiTenancy; using Volo.Abp.Settings; -using static Volo.Abp.Account.Web.Pages.Account.LoginModel; using IdentityOptions = Microsoft.AspNetCore.Identity.IdentityOptions; namespace LINGYUN.Abp.Account.Web.IdentityServer.Pages.Account @@ -53,7 +53,7 @@ namespace LINGYUN.Abp.Account.Web.IdentityServer.Pages.Account public override async Task OnGetAsync() { - LoginInput = new LoginInputModel(); + PasswordLoginInput = new PasswordLoginInputModel(); var context = await Interaction.GetAuthorizationContextAsync(ReturnUrl); @@ -62,7 +62,7 @@ namespace LINGYUN.Abp.Account.Web.IdentityServer.Pages.Account // TODO: Find a proper cancel way. // ShowCancelButton = true; - LoginInput.UserNameOrEmailAddress = context.LoginHint; + PasswordLoginInput.UserNameOrEmailAddress = context.LoginHint; //TODO: Reference AspNetCore MultiTenancy module and use options to get the tenant key! var tenant = context.Parameters[TenantResolverConsts.DefaultTenantKey]; @@ -75,7 +75,7 @@ namespace LINGYUN.Abp.Account.Web.IdentityServer.Pages.Account if (context?.IdP != null) { - LoginInput.UserNameOrEmailAddress = context.LoginHint; + PasswordLoginInput.UserNameOrEmailAddress = context.LoginHint; ExternalProviders = new[] { new ExternalLoginProviderModel { AuthenticationScheme = context.IdP } }; return Page(); } @@ -107,7 +107,7 @@ namespace LINGYUN.Abp.Account.Web.IdentityServer.Pages.Account return Page(); } - public override async Task OnPostAsync(string action) + public override async Task OnPostPasswordLogin(string action) { var context = await Interaction.GetAuthorizationContextAsync(ReturnUrl); if (action == "Cancel") @@ -138,9 +138,9 @@ namespace LINGYUN.Abp.Account.Web.IdentityServer.Pages.Account await ReplaceEmailToUsernameOfInputIfNeeds(); var result = await SignInManager.PasswordSignInAsync( - LoginInput.UserNameOrEmailAddress, - LoginInput.Password, - LoginInput.RememberMe, + PasswordLoginInput.UserNameOrEmailAddress, + PasswordLoginInput.Password, + PasswordLoginInput.RememberMe, true ); @@ -148,7 +148,7 @@ namespace LINGYUN.Abp.Account.Web.IdentityServer.Pages.Account { Identity = IdentitySecurityLogIdentityConsts.Identity, Action = result.ToIdentitySecurityLogAction(), - UserName = LoginInput.UserNameOrEmailAddress, + UserName = PasswordLoginInput.UserNameOrEmailAddress, ClientId = context?.Client?.ClientId }); @@ -173,8 +173,8 @@ namespace LINGYUN.Abp.Account.Web.IdentityServer.Pages.Account } //TODO: Find a way of getting user's id from the logged in user and do not query it again like that! - var user = await UserManager.FindByNameAsync(LoginInput.UserNameOrEmailAddress) ?? - await UserManager.FindByEmailAsync(LoginInput.UserNameOrEmailAddress); + var user = await UserManager.FindByNameAsync(PasswordLoginInput.UserNameOrEmailAddress) ?? + await UserManager.FindByEmailAsync(PasswordLoginInput.UserNameOrEmailAddress); Debug.Assert(user != null, nameof(user) + " != null"); await IdentityServerEvents.RaiseAsync(new UserLoginSuccessEvent(user.UserName, user.Id.ToString(), user.UserName)); //TODO: Use user's name once implemented diff --git a/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OpenIddict/Pages/Account/OpenIddictLoginModel.cs b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OpenIddict/Pages/Account/OpenIddictLoginModel.cs index 96bcaed97..bec7814d0 100644 --- a/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OpenIddict/Pages/Account/OpenIddictLoginModel.cs +++ b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.OpenIddict/Pages/Account/OpenIddictLoginModel.cs @@ -1,4 +1,5 @@ using LINGYUN.Abp.Account.Web.ExternalProviders; +using LINGYUN.Abp.Account.Web.Pages.Account; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Options; @@ -9,7 +10,6 @@ using Volo.Abp.DependencyInjection; using Volo.Abp.Identity; using Volo.Abp.MultiTenancy; using Volo.Abp.OpenIddict; -using static Volo.Abp.Account.Web.Pages.Account.LoginModel; using IdentityOptions = Microsoft.AspNetCore.Identity.IdentityOptions; namespace LINGYUN.Abp.Account.Web.OpenIddict.Pages.Account @@ -35,7 +35,7 @@ namespace LINGYUN.Abp.Account.Web.OpenIddict.Pages.Account public async override Task OnGetAsync() { - LoginInput = new LoginInputModel(); + PasswordLoginInput = new PasswordLoginInputModel(); var request = await OpenIddictRequestHelper.GetFromReturnUrlAsync(ReturnUrl); if (request?.ClientId != null) @@ -43,7 +43,7 @@ namespace LINGYUN.Abp.Account.Web.OpenIddict.Pages.Account // TODO: Find a proper cancel way. // ShowCancelButton = true; - LoginInput.UserNameOrEmailAddress = request.LoginHint; + PasswordLoginInput.UserNameOrEmailAddress = request.LoginHint; //TODO: Reference AspNetCore MultiTenancy module and use options to get the tenant key! var tenant = request.GetParameter(TenantResolverConsts.DefaultTenantKey)?.ToString(); diff --git a/aspnet-core/modules/account/LINGYUN.Abp.Account.Web/AbpAccountWebModule.cs b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web/AbpAccountWebModule.cs index 1c2c05fc3..827cd6c77 100644 --- a/aspnet-core/modules/account/LINGYUN.Abp.Account.Web/AbpAccountWebModule.cs +++ b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web/AbpAccountWebModule.cs @@ -1,13 +1,11 @@ using LINGYUN.Abp.Account.Emailing; using LINGYUN.Abp.Account.Web.Bundling; -using LINGYUN.Abp.Account.Web.Pages.Account; using LINGYUN.Abp.Account.Web.ProfileManagement; using LINGYUN.Abp.Identity; using LINGYUN.Abp.Identity.AspNetCore.QrCode; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Identity; -using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using System; using Volo.Abp.Account.Localization; @@ -16,7 +14,6 @@ using Volo.Abp.Account.Web.ProfileManagement; using Volo.Abp.AspNetCore.Mvc.Localization; using Volo.Abp.AspNetCore.Mvc.UI.Bundling; using Volo.Abp.AspNetCore.Mvc.UI.Packages.QRCode; -using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Bundling; using Volo.Abp.AutoMapper; using Volo.Abp.Modularity; using Volo.Abp.Sms; @@ -121,6 +118,7 @@ public class AbpAccountWebModule : AbpModule .Configure(typeof(Pages.Account.LoginModel).FullName, bundle => { bundle.AddFiles("/client-proxies/account-proxy.js"); + bundle.AddFiles("/client-proxies/qrcode-proxy.js"); bundle.AddContributors(typeof(QRCodeScriptContributor)); }); }); diff --git a/aspnet-core/modules/account/LINGYUN.Abp.Account.Web/Pages/Account/Login.cshtml b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web/Pages/Account/Login.cshtml index 3d9dd39fd..cdda0a3d3 100644 --- a/aspnet-core/modules/account/LINGYUN.Abp.Account.Web/Pages/Account/Login.cshtml +++ b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web/Pages/Account/Login.cshtml @@ -28,9 +28,11 @@