From e7799549de03b5be84c5e6e9ef28ac62f142df6b Mon Sep 17 00:00:00 2001 From: colin Date: Mon, 9 Jun 2025 11:06:39 +0800 Subject: [PATCH] =?UTF-8?q?feat(account):=20=E9=87=8D=E5=86=99=E6=B3=A8?= =?UTF-8?q?=E5=86=8C=E9=A1=B5=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 重写注册页第三方注册组件 --- .../Pages/Account/Register.cshtml | 53 ++++ .../Pages/Account/Register.cshtml.cs | 291 ++++++++++++++++++ 2 files changed, 344 insertions(+) create mode 100644 aspnet-core/modules/account/LINGYUN.Abp.Account.Web/Pages/Account/Register.cshtml create mode 100644 aspnet-core/modules/account/LINGYUN.Abp.Account.Web/Pages/Account/Register.cshtml.cs diff --git a/aspnet-core/modules/account/LINGYUN.Abp.Account.Web/Pages/Account/Register.cshtml b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web/Pages/Account/Register.cshtml new file mode 100644 index 000000000..32bcd4e95 --- /dev/null +++ b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web/Pages/Account/Register.cshtml @@ -0,0 +1,53 @@ +@page +@using Microsoft.AspNetCore.Mvc.Localization +@using Volo.Abp.Account.Localization +@model LINGYUN.Abp.Account.Web.Pages.Account.RegisterModel +@inject IHtmlLocalizer L + +
+
+

@L["Register"]

+ + @L["AlreadyRegistered"] + @L["Login"] + +
+ @if (Model.EnableLocalRegister || Model.IsExternalLogin) + { + + } + + @if (Model.EnableLocalRegister || Model.IsExternalLogin) + { + + } + + @if (!Model.IsExternalLogin && Model.EnableLocalRegister) + { + + } + + @if (Model.EnableLocalRegister || Model.IsExternalLogin) + { +
+ @L["Register"] +
+ } + + + + @if (!Model.IsExternalLogin && Model.VisibleExternalProviders.Any()) + { +
+
@L["OrRegisterWith"]
+
+ @foreach (var provider in Model.VisibleExternalProviders) + { + @await Component.InvokeAsync(provider.ComponentType, provider); + } +
+
+ } + +
+
diff --git a/aspnet-core/modules/account/LINGYUN.Abp.Account.Web/Pages/Account/Register.cshtml.cs b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web/Pages/Account/Register.cshtml.cs new file mode 100644 index 000000000..1ba76bb5f --- /dev/null +++ b/aspnet-core/modules/account/LINGYUN.Abp.Account.Web/Pages/Account/Register.cshtml.cs @@ -0,0 +1,291 @@ +using LINGYUN.Abp.Account.Web.ExternalProviders; +using LINGYUN.Abp.Account.Web.Models; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Security.Claims; +using System.Threading.Tasks; +using Volo.Abp; +using Volo.Abp.Account; +using Volo.Abp.Account.Settings; +using Volo.Abp.Account.Web; +using Volo.Abp.Account.Web.Pages.Account; +using Volo.Abp.Auditing; +using Volo.Abp.Identity; +using Volo.Abp.Reflection; +using Volo.Abp.Security.Claims; +using Volo.Abp.Settings; +using Volo.Abp.Validation; +using IAbpAccountAppService = Volo.Abp.Account.IAccountAppService; +using IdentityUser = Volo.Abp.Identity.IdentityUser; + +namespace LINGYUN.Abp.Account.Web.Pages.Account; + +public class RegisterModel : AccountPageModel +{ + + [BindProperty(SupportsGet = true)] + public string ReturnUrl { get; set; } + + [BindProperty(SupportsGet = true)] + public string ReturnUrlHash { get; set; } + + [BindProperty] + public PostInput Input { get; set; } + + [BindProperty(SupportsGet = true)] + public bool IsExternalLogin { get; set; } + + [BindProperty(SupportsGet = true)] + public string ExternalLoginAuthSchema { get; set; } + + public IEnumerable ExternalProviders { get; set; } + public IEnumerable VisibleExternalProviders => ExternalProviders.Where(x => !string.IsNullOrWhiteSpace(x.DisplayName)); + public bool EnableLocalRegister { get; set; } + public bool IsExternalLoginOnly => EnableLocalRegister == false && ExternalProviders?.Count() == 1; + public string ExternalLoginScheme => IsExternalLoginOnly ? ExternalProviders?.SingleOrDefault()?.AuthenticationScheme : null; + + protected IExternalProviderService ExternalProviderService { get; } + protected IAuthenticationSchemeProvider SchemeProvider { get; } + + protected AbpAccountOptions AccountOptions { get; } + protected IdentityDynamicClaimsPrincipalContributorCache IdentityDynamicClaimsPrincipalContributorCache { get; } + + public RegisterModel( + IExternalProviderService externalProviderService, + IAbpAccountAppService accountAppService, + IAuthenticationSchemeProvider schemeProvider, + IOptions accountOptions, + IdentityDynamicClaimsPrincipalContributorCache identityDynamicClaimsPrincipalContributorCache) + { + ExternalProviderService = externalProviderService; + SchemeProvider = schemeProvider; + IdentityDynamicClaimsPrincipalContributorCache = identityDynamicClaimsPrincipalContributorCache; + AccountAppService = accountAppService; + AccountOptions = accountOptions.Value; + } + + public virtual async Task OnGetAsync() + { + ExternalProviders = await GetExternalProviders(); + + if (!await CheckSelfRegistrationAsync()) + { + if (IsExternalLoginOnly) + { + return await OnPostExternalLogin(ExternalLoginScheme); + } + + Alerts.Warning(L["SelfRegistrationDisabledMessage"]); + } + + await TrySetEmailAsync(); + + return Page(); + } + + protected virtual async Task TrySetEmailAsync() + { + if (IsExternalLogin) + { + var externalLoginInfo = await SignInManager.GetExternalLoginInfoAsync(); + if (externalLoginInfo == null) + { + return; + } + + if (!externalLoginInfo.Principal.Identities.Any()) + { + return; + } + + var identity = externalLoginInfo.Principal.Identities.First(); + var emailClaim = identity.FindFirst(AbpClaimTypes.Email) ?? identity.FindFirst(ClaimTypes.Email); + + if (emailClaim == null) + { + return; + } + + var userName = await UserManager.GetUserNameFromEmailAsync(emailClaim.Value); + Input = new PostInput { UserName = userName, EmailAddress = emailClaim.Value }; + } + } + + public virtual async Task OnPostAsync() + { + try + { + ExternalProviders = await GetExternalProviders(); + + if (!await CheckSelfRegistrationAsync()) + { + throw new UserFriendlyException(L["SelfRegistrationDisabledMessage"]); + } + + if (IsExternalLogin) + { + var externalLoginInfo = await SignInManager.GetExternalLoginInfoAsync(); + if (externalLoginInfo == null) + { + Logger.LogWarning("External login info is not available"); + return RedirectToPage("./Login"); + } + if (Input.UserName.IsNullOrWhiteSpace()) + { + Input.UserName = await UserManager.GetUserNameFromEmailAsync(Input.EmailAddress); + } + await RegisterExternalUserAsync(externalLoginInfo, Input.UserName, Input.EmailAddress); + } + else + { + await RegisterLocalUserAsync(); + } + + return Redirect(ReturnUrl ?? "~/"); //TODO: How to ensure safety? IdentityServer requires it however it should be checked somehow! + } + catch (BusinessException e) + { + Alerts.Danger(GetLocalizeExceptionMessage(e)); + return Page(); + } + } + + protected virtual async Task RegisterLocalUserAsync() + { + ValidateModel(); + + var userDto = await AccountAppService.RegisterAsync( + new RegisterDto + { + AppName = "MVC", + EmailAddress = Input.EmailAddress, + Password = Input.Password, + UserName = Input.UserName + } + ); + + var user = await UserManager.GetByIdAsync(userDto.Id); + await SignInManager.SignInAsync(user, isPersistent: true); + + // Clear the dynamic claims cache. + await IdentityDynamicClaimsPrincipalContributorCache.ClearAsync(user.Id, user.TenantId); + } + + protected virtual async Task RegisterExternalUserAsync(ExternalLoginInfo externalLoginInfo, string userName, string emailAddress) + { + await IdentityOptions.SetAsync(); + + var user = new IdentityUser(GuidGenerator.Create(), userName, emailAddress, CurrentTenant.Id); + + (await UserManager.CreateAsync(user)).CheckErrors(); + (await UserManager.AddDefaultRolesAsync(user)).CheckErrors(); + + var userLoginAlreadyExists = user.Logins.Any(x => + x.TenantId == user.TenantId && + x.LoginProvider == externalLoginInfo.LoginProvider && + x.ProviderKey == externalLoginInfo.ProviderKey); + + if (!userLoginAlreadyExists) + { + (await UserManager.AddLoginAsync(user, new UserLoginInfo( + externalLoginInfo.LoginProvider, + externalLoginInfo.ProviderKey, + externalLoginInfo.ProviderDisplayName + ))).CheckErrors(); + } + + await SignInManager.SignInAsync(user, isPersistent: true, ExternalLoginAuthSchema); + + // Clear the dynamic claims cache. + await IdentityDynamicClaimsPrincipalContributorCache.ClearAsync(user.Id, user.TenantId); + } + + protected virtual async Task CheckSelfRegistrationAsync() + { + EnableLocalRegister = await SettingProvider.IsTrueAsync(AccountSettingNames.EnableLocalLogin) && + await SettingProvider.IsTrueAsync(AccountSettingNames.IsSelfRegistrationEnabled); + + if (IsExternalLogin) + { + return true; + } + + if (!EnableLocalRegister) + { + return false; + } + + return true; + } + + protected async virtual Task> GetExternalProviders() + { + var schemes = await SchemeProvider.GetAllSchemesAsync(); + var externalProviders = await ExternalProviderService.GetAllAsync(); + + var externalProviderModels = new List(); + foreach (var scheme in schemes) + { + if (TryGetExternalLoginProvider(scheme, externalProviders, out var externalLoginProvider) || + scheme.Name.Equals(AccountOptions.WindowsAuthenticationSchemeName, StringComparison.OrdinalIgnoreCase)) + { + externalProviderModels.Add(new ExternalLoginProviderModel + { + Name = externalLoginProvider.Name, + AuthenticationScheme = scheme.Name, + DisplayName = externalLoginProvider.DisplayName, + ComponentType = externalLoginProvider.ComponentType, + }); + } + } + + return externalProviderModels; + } + + protected virtual bool TryGetExternalLoginProvider(AuthenticationScheme scheme, List externalProviders, out ExternalLoginProviderModel externalLoginProvider) + { + if (ReflectionHelper.IsAssignableToGenericType(scheme.HandlerType, typeof(RemoteAuthenticationHandler<>))) + { + externalLoginProvider = externalProviders.FirstOrDefault(x => x.Name == scheme.Name); + return externalLoginProvider != null; + } + + externalLoginProvider = null; + return false; + } + + protected virtual async Task OnPostExternalLogin(string provider) + { + var redirectUrl = Url.Page("./Login", pageHandler: "ExternalLoginCallback", values: new { ReturnUrl, ReturnUrlHash }); + var properties = SignInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl); + properties.Items["scheme"] = provider; + + return await Task.FromResult(Challenge(properties, provider)); + } + + public class PostInput + { + [Required] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxUserNameLength))] + public string UserName { get; set; } + + [Required] + [EmailAddress] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxEmailLength))] + public string EmailAddress { get; set; } + + [Required] + [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxPasswordLength))] + [DataType(DataType.Password)] + [DisableAuditing] + public string Password { get; set; } + } +} +