From fdcf4d7b0174d8f739ee48aef66591ac5fa7bea0 Mon Sep 17 00:00:00 2001 From: colin Date: Fri, 6 Jun 2025 08:08:51 +0800 Subject: [PATCH] fix(identity-server): Fixed identityServer login model - Modify base class source --- .../Account/TwoFactorSupportedLoginModel.cs | 179 ++++++++++++++++-- 1 file changed, 159 insertions(+), 20 deletions(-) diff --git a/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.IdentityServer/Pages/Account/TwoFactorSupportedLoginModel.cs b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.IdentityServer/Pages/Account/TwoFactorSupportedLoginModel.cs index 29a46ffd6..a10306ef5 100644 --- a/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.IdentityServer/Pages/Account/TwoFactorSupportedLoginModel.cs +++ b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web.IdentityServer/Pages/Account/TwoFactorSupportedLoginModel.cs @@ -1,14 +1,23 @@ -using IdentityServer4.Services; +using IdentityServer4.Events; +using IdentityServer4.Models; +using IdentityServer4.Services; using IdentityServer4.Stores; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Options; +using System; using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; using System.Threading.Tasks; +using Volo.Abp.Account.Settings; using Volo.Abp.Account.Web; using Volo.Abp.Account.Web.Pages.Account; using Volo.Abp.DependencyInjection; using Volo.Abp.Identity; +using Volo.Abp.IdentityServer.AspNetIdentity; +using Volo.Abp.MultiTenancy; +using Volo.Abp.Settings; using IdentityOptions = Microsoft.AspNetCore.Identity.IdentityOptions; namespace LINGYUN.Abp.Account.Web.IdentityServer.Pages.Account @@ -17,20 +26,161 @@ namespace LINGYUN.Abp.Account.Web.IdentityServer.Pages.Account /// 重写登录模型,实现双因素登录 /// [Dependency(ReplaceServices = true)] - [ExposeServices(typeof(LoginModel), typeof(IdentityServerSupportedLoginModel))] - public class TwoFactorSupportedLoginModel : IdentityServerSupportedLoginModel + [ExposeServices( + typeof(LINGYUN.Abp.Account.Web.Pages.Account.LoginModel), + typeof(IdentityServerSupportedLoginModel))] + public class TwoFactorSupportedLoginModel : LINGYUN.Abp.Account.Web.Pages.Account.LoginModel { + protected IIdentityServerInteractionService Interaction { get; } + protected IEventService IdentityServerEvents { get; } + protected IClientStore ClientStore { get; } public TwoFactorSupportedLoginModel( - IAuthenticationSchemeProvider schemeProvider, - IOptions accountOptions, + IAuthenticationSchemeProvider schemeProvider, + IOptions accountOptions, IOptions identityOptions, - IIdentityServerInteractionService interaction, IdentityDynamicClaimsPrincipalContributorCache identityDynamicClaimsPrincipalContributorCache, - IClientStore clientStore, - IEventService identityServerEvents) - : base(schemeProvider, accountOptions, identityOptions, identityDynamicClaimsPrincipalContributorCache, interaction, clientStore, identityServerEvents) + IIdentityServerInteractionService interaction, + IEventService identityServerEvents, + IClientStore clientStore) + : base(schemeProvider, accountOptions, identityOptions, identityDynamicClaimsPrincipalContributorCache) + { + Interaction = interaction; + ClientStore = clientStore; + IdentityServerEvents = identityServerEvents; + } + + public override async Task OnGetAsync() { + LoginInput = new LoginInputModel(); + + var context = await Interaction.GetAuthorizationContextAsync(ReturnUrl); + + if (context != null) + { + // TODO: Find a proper cancel way. + // ShowCancelButton = true; + + LoginInput.UserNameOrEmailAddress = context.LoginHint; + + //TODO: Reference AspNetCore MultiTenancy module and use options to get the tenant key! + var tenant = context.Parameters[TenantResolverConsts.DefaultTenantKey]; + if (!string.IsNullOrEmpty(tenant)) + { + CurrentTenant.Change(Guid.Parse(tenant)); + Response.Cookies.Append(TenantResolverConsts.DefaultTenantKey, tenant); + } + } + + if (context?.IdP != null) + { + LoginInput.UserNameOrEmailAddress = context.LoginHint; + ExternalProviders = new[] { new ExternalProviderModel { AuthenticationScheme = context.IdP } }; + return Page(); + } + + var providers = await GetExternalProviders(); + ExternalProviders = providers.ToList(); + + EnableLocalLogin = await SettingProvider.IsTrueAsync(AccountSettingNames.EnableLocalLogin); + + if (context?.Client?.ClientId != null) + { + var client = await ClientStore.FindEnabledClientByIdAsync(context?.Client?.ClientId); + if (client != null) + { + EnableLocalLogin = client.EnableLocalLogin; + + if (client.IdentityProviderRestrictions != null && client.IdentityProviderRestrictions.Any()) + { + providers = providers.Where(provider => client.IdentityProviderRestrictions.Contains(provider.AuthenticationScheme)).ToList(); + } + } + } + + if (IsExternalLoginOnly) + { + return await base.OnPostExternalLogin(providers.First().AuthenticationScheme); + } + + return Page(); + } + + public override async Task OnPostAsync(string action) + { + var context = await Interaction.GetAuthorizationContextAsync(ReturnUrl); + if (action == "Cancel") + { + if (context == null) + { + return Redirect("~/"); + } + + await Interaction.GrantConsentAsync(context, new ConsentResponse() + { + Error = AuthorizationError.AccessDenied + }); + + return Redirect(ReturnUrl); + } + + await CheckLocalLoginAsync(); + + ValidateModel(); + + await IdentityOptions.SetAsync(); + + ExternalProviders = await GetExternalProviders(); + + EnableLocalLogin = await SettingProvider.IsTrueAsync(AccountSettingNames.EnableLocalLogin); + + await ReplaceEmailToUsernameOfInputIfNeeds(); + + var result = await SignInManager.PasswordSignInAsync( + LoginInput.UserNameOrEmailAddress, + LoginInput.Password, + LoginInput.RememberMe, + true + ); + + await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext() + { + Identity = IdentitySecurityLogIdentityConsts.Identity, + Action = result.ToIdentitySecurityLogAction(), + UserName = LoginInput.UserNameOrEmailAddress, + ClientId = context?.Client?.ClientId + }); + + if (result.RequiresTwoFactor) + { + return await TwoFactorLoginResultAsync(); + } + if (result.IsLockedOut) + { + return await HandleUserLockedOut(); + } + + if (result.IsNotAllowed) + { + return await HandleUserNotAllowed(); + } + + if (!result.Succeeded) + { + return await HandleUserNameOrPasswordInvalid(); + } + + //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); + + 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 + + // Clear the dynamic claims cache. + await IdentityDynamicClaimsPrincipalContributorCache.ClearAsync(user.Id, user.TenantId); + + return await RedirectSafelyAsync(ReturnUrl, ReturnUrlHash); } protected async override Task> GetExternalProviders() @@ -53,16 +203,5 @@ namespace LINGYUN.Abp.Account.Web.IdentityServer.Pages.Account return providers; } - - protected override Task TwoFactorLoginResultAsync() - { - // 重定向双因素认证页面 - return Task.FromResult(RedirectToPage("SendCode", new - { - returnUrl = ReturnUrl, - returnUrlHash = ReturnUrlHash, - rememberMe = LoginInput.RememberMe - })); - } } }