From c9b63289199f7062ff6ff1e19cafebe7fd9feaa7 Mon Sep 17 00:00:00 2001 From: colin Date: Thu, 21 Aug 2025 18:00:15 +0800 Subject: [PATCH 1/2] feat(account): Add phone verification code login - add new localized text - Use tab to switch between different login forms --- .../Account/Localization/Resources/en.json | 6 +- .../Localization/Resources/zh-Hans.json | 6 +- .../AbpAccountWebModule.cs | 21 ++- .../Mvc/ModelBinding/ModelStateExtensions.cs | 27 +++ .../Pages/Account/Login.cshtml | 169 ++++++++++-------- .../Pages/Account/Login.cshtml.cs | 149 ++++++++++++--- .../Pages/Account/Login.js | 54 ++++++ 7 files changed, 326 insertions(+), 106 deletions(-) create mode 100644 aspnet-core/modules/account/LINGYUN.Abp.Account.Web/Microsoft/AspNetCore/Mvc/ModelBinding/ModelStateExtensions.cs create mode 100644 aspnet-core/modules/account/LINGYUN.Abp.Account.Web/Pages/Account/Login.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 4c6527801..b0f6e7cbc 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 @@ -51,6 +51,10 @@ "ProfileTab:Authenticator": "Authenticator", "ProfileTab:SecurityLog": "Security Log", "PhoneNumberChangedMessage": "Your mobile number has been successfully changed.", - "SecurityLogs": "Security Logs" + "SecurityLogs": "Security Logs", + "PasswordLogin": "Login with password", + "PhoneNumberLogin": "Login with phone", + "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." } } \ No newline at end of file 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 86f3b2b45..527f80541 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 @@ -51,6 +51,10 @@ "ProfileTab:Authenticator": "身份验证程序", "ProfileTab:SecurityLog": "安全日志", "PhoneNumberChangedMessage": "您的手机号码已成功更改.", - "SecurityLogs": "安全日志" + "SecurityLogs": "安全日志", + "PasswordLogin": "密码登录", + "PhoneNumberLogin": "验证码登录", + "DisplayName:Abp.Account.EnablePhoneNumberLogin": "使用手机验证码进行身份验证", + "Description:Abp.Account.EnablePhoneNumberLogin": "表示服务器是否允许用户使用手机验证码进行身份验证。" } } \ No newline at end of file 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 0b2b446b2..1c2c05fc3 100644 --- a/aspnet-core/modules/account/LINGYUN.Abp.Account.Web/AbpAccountWebModule.cs +++ b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web/AbpAccountWebModule.cs @@ -7,6 +7,7 @@ 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; @@ -91,31 +92,37 @@ public class AbpAccountWebModule : AbpModule options.ScriptBundles .Configure(typeof(ManageModel).FullName, - configuration => + bundle => { // Client Proxies - configuration.AddFiles("/client-proxies/account-proxy.js"); + bundle.AddFiles("/client-proxies/account-proxy.js"); // Session - configuration.AddFiles("/Pages/Account/Components/ProfileManagementGroup/Session/Index.js"); + bundle.AddFiles("/Pages/Account/Components/ProfileManagementGroup/Session/Index.js"); // Authenticator - configuration.AddFiles("/Pages/Account/Components/ProfileManagementGroup/Authenticator/Index.js"); + bundle.AddFiles("/Pages/Account/Components/ProfileManagementGroup/Authenticator/Index.js"); // SecurityLog - configuration.AddFiles("/Pages/Account/Components/ProfileManagementGroup/SecurityLog/Index.js"); + bundle.AddFiles("/Pages/Account/Components/ProfileManagementGroup/SecurityLog/Index.js"); // TwoFactor - configuration.AddFiles("/Pages/Account/Components/ProfileManagementGroup/TwoFactor/Default.js"); + bundle.AddFiles("/Pages/Account/Components/ProfileManagementGroup/TwoFactor/Default.js"); // QrCode - configuration.AddContributors(typeof(QRCodeScriptContributor)); + bundle.AddContributors(typeof(QRCodeScriptContributor)); }); options.ScriptBundles .Configure(AccountBundles.Scripts.ChangePassword, bundle => { bundle.AddContributors(typeof(ChangePasswordScriptContributor)); }); + options.ScriptBundles + .Configure(typeof(Pages.Account.LoginModel).FullName, bundle => + { + bundle.AddFiles("/client-proxies/account-proxy.js"); + bundle.AddContributors(typeof(QRCodeScriptContributor)); + }); }); } } diff --git a/aspnet-core/modules/account/LINGYUN.Abp.Account.Web/Microsoft/AspNetCore/Mvc/ModelBinding/ModelStateExtensions.cs b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web/Microsoft/AspNetCore/Mvc/ModelBinding/ModelStateExtensions.cs new file mode 100644 index 000000000..4f72500b8 --- /dev/null +++ b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web/Microsoft/AspNetCore/Mvc/ModelBinding/ModelStateExtensions.cs @@ -0,0 +1,27 @@ +using System.Linq; + +namespace Microsoft.AspNetCore.Mvc.ModelBinding; + +public static class ModelStateExtensions +{ + public static void RemoveModelErrors(this ModelStateDictionary modelState, string modelName) + { + var keys = modelState.Keys + .Where(k => k.StartsWith(modelName + ".") || k == modelName) + .ToList(); + + foreach (var key in keys) + { + modelState.Remove(key); + } + } + + public static bool IsValidForModel(this ModelStateDictionary modelState, string modelName) + { + modelState.RemoveModelErrors(modelName); + + return modelState.Keys + .Where(k => k.StartsWith(modelName + ".") || k == modelName) + .All(key => modelState[key].ValidationState == ModelValidationState.Valid); + } +} 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 f69fe2973..3d9dd39fd 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 @@ -1,18 +1,21 @@ @page +@using LINGYUN.Abp.Account.Web.Pages.Account @using Microsoft.AspNetCore.Mvc.Localization @using Volo.Abp.Account.Localization @using Volo.Abp.Account.Settings -@using Volo.Abp.Account.Web.Pages.Account; +@using Volo.Abp.AspNetCore.Mvc.UI.Layout @using Volo.Abp.AspNetCore.Mvc.UI.Theming; @using Volo.Abp.Identity; -@using Volo.Abp.Settings +@using Volo.Abp.Settings; @model LINGYUN.Abp.Account.Web.Pages.Account.LoginModel @inject IHtmlLocalizer L @inject IThemeManager ThemeManager +@inject IPageLayout PageLayout @inject Volo.Abp.Settings.ISettingProvider SettingProvider @{ Layout = ThemeManager.CurrentTheme.GetAccountLayout(); + PageLayout.Content.Title = L["Login"].Value; } @section scripts @@ -22,84 +25,102 @@ } -
-
-

@L["Login"]

- @if (Model.EnableLocalLogin) - { -
-
- - - -
+
diff --git a/aspnet-core/modules/account/LINGYUN.Abp.Account.Web/Pages/Account/Login.cshtml.cs b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web/Pages/Account/Login.cshtml.cs index eb331a83a..290362474 100644 --- a/aspnet-core/modules/account/LINGYUN.Abp.Account.Web/Pages/Account/Login.cshtml.cs +++ b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web/Pages/Account/Login.cshtml.cs @@ -3,10 +3,12 @@ using LINGYUN.Abp.Account.Web.Models; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using System; using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; using System.Diagnostics; using System.Linq; using System.Security.Claims; @@ -15,7 +17,7 @@ using Volo.Abp; using Volo.Abp.Account.Settings; using Volo.Abp.Account.Web; using Volo.Abp.Account.Web.Pages.Account; -using Volo.Abp.DependencyInjection; +using Volo.Abp.Auditing; using Volo.Abp.Identity; using Volo.Abp.Identity.AspNetCore; using Volo.Abp.Reflection; @@ -24,10 +26,10 @@ using Volo.Abp.Settings; using Volo.Abp.Validation; using static Volo.Abp.Account.Web.Pages.Account.LoginModel; using IdentityUser = Volo.Abp.Identity.IdentityUser; +using IIdentityUserRepository = LINGYUN.Abp.Identity.IIdentityUserRepository; namespace LINGYUN.Abp.Account.Web.Pages.Account; -//[ExposeServices(typeof(Volo.Abp.Account.Web.Pages.Account.LoginModel))] public class LoginModel : AccountPageModel { [HiddenInput] @@ -38,8 +40,15 @@ public class LoginModel : AccountPageModel [BindProperty(SupportsGet = true)] public string ReturnUrlHash { get; set; } - [BindProperty] - public LoginInputModel LoginInput { get; set; } + [HiddenInput] + [BindProperty(SupportsGet = true)] + public LoginType LoginType { get; set; } + + [BindProperty(Name = "PasswordLoginInput")] + public PasswordLoginInputModel PasswordLoginInput { get; set; } + + [BindProperty(Name = "PhoneLoginInput")] + public PhoneLoginInputModel PhoneLoginInput { get; set; } public bool EnableLocalLogin { get; set; } @@ -50,6 +59,7 @@ public class LoginModel : AccountPageModel public IEnumerable ExternalProviders { get; set; } public IEnumerable VisibleExternalProviders => ExternalProviders.Where(x => !x.DisplayName.IsNullOrWhiteSpace()); + protected IIdentityUserRepository UserRepository => LazyServiceProvider.LazyGetRequiredService(); protected IExternalProviderService ExternalProviderService { get; } protected IAuthenticationSchemeProvider SchemeProvider { get; } @@ -71,7 +81,9 @@ public class LoginModel : AccountPageModel public virtual async Task OnGetAsync() { - LoginInput = new LoginInputModel(); + LoginType = LoginType.Password; + PhoneLoginInput = new PhoneLoginInputModel(); + PasswordLoginInput = new PasswordLoginInputModel(); ExternalProviders = await GetExternalProviders(); @@ -85,24 +97,30 @@ public class LoginModel : AccountPageModel return Page(); } - public async virtual Task OnPostAsync(string action) + public async virtual Task OnPostPasswordLogin(string action) { - await CheckLocalLoginAsync(); + LoginType = LoginType.Password; - ValidateModel(); + await CheckLocalLoginAsync(); ExternalProviders = await GetExternalProviders(); EnableLocalLogin = await SettingProvider.IsTrueAsync(AccountSettingNames.EnableLocalLogin); + ModelState.RemoveModelErrors(nameof(PhoneLoginInput)); + if (!TryValidateModel(PasswordLoginInput, nameof(PasswordLoginInput))) + { + return Page(); + } + await ReplaceEmailToUsernameOfInputIfNeeds(); await IdentityOptions.SetAsync(); var result = await SignInManager.PasswordSignInAsync( - LoginInput.UserNameOrEmailAddress, - LoginInput.Password, - LoginInput.RememberMe, + PasswordLoginInput.UserNameOrEmailAddress, + PasswordLoginInput.Password, + PasswordLoginInput.RememberMe, true ); @@ -110,7 +128,7 @@ public class LoginModel : AccountPageModel { Identity = IdentitySecurityLogIdentityConsts.Identity, Action = result.ToIdentitySecurityLogAction(), - UserName = LoginInput.UserNameOrEmailAddress + UserName = PasswordLoginInput.UserNameOrEmailAddress }); if (result.RequiresTwoFactor) @@ -134,7 +152,7 @@ public class LoginModel : AccountPageModel } //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 GetIdentityUserAsync(LoginInput.UserNameOrEmailAddress); + var user = await GetIdentityUserAsync(PasswordLoginInput.UserNameOrEmailAddress); Debug.Assert(user != null, nameof(user) + " != null"); @@ -144,6 +162,54 @@ public class LoginModel : AccountPageModel return await RedirectSafelyAsync(ReturnUrl, ReturnUrlHash); } + public async virtual Task OnPostPhoneNumberLogin(string action) + { + LoginType = LoginType.PhoneNumber; + + await CheckLocalLoginAsync(); + + ExternalProviders = await GetExternalProviders(); + + EnableLocalLogin = await SettingProvider.IsTrueAsync(AccountSettingNames.EnableLocalLogin); + + ModelState.RemoveModelErrors(nameof(PasswordLoginInput)); + if (!TryValidateModel(PhoneLoginInput, nameof(PhoneLoginInput))) + { + return Page(); + } + + var user = await UserRepository.FindByPhoneNumberAsync(PhoneLoginInput.PhoneNumber); + if (user == null) + { + Logger.LogInformation("the user phone number is not registed!"); + Alerts.Danger(L["InvalidPhoneNumber"]); + return Page(); + } + + var result = await UserManager.VerifyTwoFactorTokenAsync(user, TokenOptions.DefaultPhoneProvider, PhoneLoginInput.Code); + if (!result) + { + Alerts.Danger(L["InvalidVerifyCode"]); + return Page(); + } + + await SignInManager.SignInAsync( + user, + PasswordLoginInput.RememberMe); + + await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext() + { + Identity = IdentitySecurityLogIdentityConsts.IdentityTwoFactor, + Action = IdentitySecurityLogActionConsts.LoginSucceeded, + UserName = user.UserName + }); + + // Clear the dynamic claims cache. + await IdentityDynamicClaimsPrincipalContributorCache.ClearAsync(user.Id, user.TenantId); + + return await RedirectSafelyAsync(ReturnUrl, ReturnUrlHash); + } + public virtual async Task OnPostExternalLogin(string provider) { var redirectUrl = Url.Page("./Login", pageHandler: "ExternalLoginCallback", values: new { ReturnUrl, ReturnUrlHash }); @@ -267,15 +333,15 @@ public class LoginModel : AccountPageModel { returnUrl = ReturnUrl, returnUrlHash = ReturnUrlHash, - rememberMe = LoginInput.RememberMe + rememberMe = PasswordLoginInput.RememberMe })); } protected virtual async Task GetIdentityUserAsync(string userNameOrEmailAddress) { - return await UserManager.FindByNameAsync(LoginInput.UserNameOrEmailAddress) ?? - await UserManager.FindByEmailAsync(LoginInput.UserNameOrEmailAddress); + return await UserManager.FindByNameAsync(PasswordLoginInput.UserNameOrEmailAddress) ?? + await UserManager.FindByEmailAsync(PasswordLoginInput.UserNameOrEmailAddress); } protected async virtual Task> GetExternalProviders() @@ -316,24 +382,24 @@ public class LoginModel : AccountPageModel protected virtual async Task ReplaceEmailToUsernameOfInputIfNeeds() { - if (!ValidationHelper.IsValidEmailAddress(LoginInput.UserNameOrEmailAddress)) + if (!ValidationHelper.IsValidEmailAddress(PasswordLoginInput.UserNameOrEmailAddress)) { return; } - var userByUsername = await UserManager.FindByNameAsync(LoginInput.UserNameOrEmailAddress); + var userByUsername = await UserManager.FindByNameAsync(PasswordLoginInput.UserNameOrEmailAddress); if (userByUsername != null) { return; } - var userByEmail = await UserManager.FindByEmailAsync(LoginInput.UserNameOrEmailAddress); + var userByEmail = await UserManager.FindByEmailAsync(PasswordLoginInput.UserNameOrEmailAddress); if (userByEmail == null) { return; } - LoginInput.UserNameOrEmailAddress = userByEmail.UserName; + PasswordLoginInput.UserNameOrEmailAddress = userByEmail.UserName; } protected virtual async Task CheckLocalLoginAsync() @@ -352,8 +418,8 @@ public class LoginModel : AccountPageModel protected async virtual Task HandleUserNotAllowed() { - var notAllowedUser = await GetIdentityUserAsync(LoginInput.UserNameOrEmailAddress); - if (await UserManager.CheckPasswordAsync(notAllowedUser, LoginInput.Password)) + var notAllowedUser = await GetIdentityUserAsync(PasswordLoginInput.UserNameOrEmailAddress); + if (await UserManager.CheckPasswordAsync(notAllowedUser, PasswordLoginInput.Password)) { // û޸ if (notAllowedUser.ShouldChangePasswordOnNextLogin || await UserManager.ShouldPeriodicallyChangePasswordAsync(notAllowedUser)) @@ -371,7 +437,7 @@ public class LoginModel : AccountPageModel { returnUrl = ReturnUrl, returnUrlHash = ReturnUrlHash, - rememberMe = LoginInput.RememberMe + rememberMe = PasswordLoginInput.RememberMe }); } } @@ -385,3 +451,40 @@ public class LoginModel : AccountPageModel return Task.FromResult(Page()); } } + +public class PhoneLoginInputModel +{ + [Phone] + [Required] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxPhoneNumberLength))] + public string PhoneNumber { get; set; } + + [Required] + [StringLength(6)] + [Display(Name = "VerifyCode")] + public string Code { get; set; } + + public bool RememberMe { get; set; } +} + +public class PasswordLoginInputModel +{ + [Required] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxEmailLength))] + public string UserNameOrEmailAddress { get; set; } + + [Required] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxPasswordLength))] + [DataType(DataType.Password)] + [DisableAuditing] + public string Password { get; set; } + + public bool RememberMe { get; set; } +} + +public enum LoginType +{ + Password = 0, + PhoneNumber = 1, + QrCode = 2 +} diff --git a/aspnet-core/modules/account/LINGYUN.Abp.Account.Web/Pages/Account/Login.js b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web/Pages/Account/Login.js new file mode 100644 index 000000000..efe347fb5 --- /dev/null +++ b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web/Pages/Account/Login.js @@ -0,0 +1,54 @@ +$(function () { + let timer; + let countDown = 0; + var l = abp.localization.getResource('AbpAccount'); + var authService = labp.account.account; + + $("#SendVerifyCodeButton").click(function (e) { + const button = $(this); + e.preventDefault(); + + var isValid = $('#PhoneNumberForm').validate().element('#PhoneNumberInput'); + if (!isValid) { + return false; + } + + var input = $('#PhoneNumberForm').serializeFormToObject(); + + authService.sendPhoneSigninCode({ + phoneNumber: input.phoneLoginInput.phoneNumber, + }).then(function () { + countDown = 60; + timer = setInterval(function () { + button.prop('disabled', true); + button.text(`${countDown}`); + if (countDown === 0) { + clearInterval(timer); + button.prop('disabled', false); + button.text(l('SendVerifyCode')); + } + countDown--; + }, 1000); + }); + }); + + $("#PasswordVisibilityButton").click(function (e) { + let button = $(this); + let passwordInput = button.parent().find("input"); + if (!passwordInput) { + return; + } + + if (passwordInput.attr("type") === "password") { + passwordInput.attr("type", "text"); + } + else { + passwordInput.attr("type", "password"); + } + + let icon = button.find("i"); + if (icon) { + icon.toggleClass("fa-eye-slash").toggleClass("fa-eye"); + } + }); +}); From 5649da83bcd615ece3d504630d845ff6b78bb849 Mon Sep 17 00:00:00 2001 From: colin Date: Mon, 25 Aug 2025 07:49:45 +0800 Subject: [PATCH 2/2] 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 @@