committed by
GitHub
2 changed files with 344 additions and 0 deletions
@ -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<AccountResource> L |
|||
|
|||
<div class="card mt-3 shadow-sm rounded"> |
|||
<div class="card-body p-5"> |
|||
<h4>@L["Register"]</h4> |
|||
<strong> |
|||
@L["AlreadyRegistered"] |
|||
<a href="@Url.Page("./Login", new {returnUrl = Model.ReturnUrl, returnUrlHash = Model.ReturnUrlHash})" class="text-decoration-none">@L["Login"]</a> |
|||
</strong> |
|||
<form method="post" class="mt-4"> |
|||
@if (Model.EnableLocalRegister || Model.IsExternalLogin) |
|||
{ |
|||
<abp-input asp-for="Input.UserName" auto-focus="true" /> |
|||
} |
|||
|
|||
@if (Model.EnableLocalRegister || Model.IsExternalLogin) |
|||
{ |
|||
<abp-input asp-for="Input.EmailAddress" /> |
|||
} |
|||
|
|||
@if (!Model.IsExternalLogin && Model.EnableLocalRegister) |
|||
{ |
|||
<abp-input asp-for="Input.Password" /> |
|||
} |
|||
|
|||
@if (Model.EnableLocalRegister || Model.IsExternalLogin) |
|||
{ |
|||
<div class="d-grid gap-2"> |
|||
<abp-button button-type="Primary" type="submit" class="btn-lg mt-4">@L["Register"]</abp-button> |
|||
</div> |
|||
} |
|||
|
|||
</form> |
|||
|
|||
@if (!Model.IsExternalLogin && Model.VisibleExternalProviders.Any()) |
|||
{ |
|||
<div class="mt-2"> |
|||
<h5>@L["OrRegisterWith"]</h5> |
|||
<form asp-page="./Login" asp-page-handler="ExternalLogin" asp-route-returnUrl="@Model.ReturnUrl" asp-route-returnUrlHash="@Model.ReturnUrlHash" method="post"> |
|||
@foreach (var provider in Model.VisibleExternalProviders) |
|||
{ |
|||
@await Component.InvokeAsync(provider.ComponentType, provider); |
|||
} |
|||
</form> |
|||
</div> |
|||
} |
|||
|
|||
</div> |
|||
</div> |
|||
@ -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<ExternalLoginProviderModel> ExternalProviders { get; set; } |
|||
public IEnumerable<ExternalLoginProviderModel> 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<AbpAccountOptions> accountOptions, |
|||
IdentityDynamicClaimsPrincipalContributorCache identityDynamicClaimsPrincipalContributorCache) |
|||
{ |
|||
ExternalProviderService = externalProviderService; |
|||
SchemeProvider = schemeProvider; |
|||
IdentityDynamicClaimsPrincipalContributorCache = identityDynamicClaimsPrincipalContributorCache; |
|||
AccountAppService = accountAppService; |
|||
AccountOptions = accountOptions.Value; |
|||
} |
|||
|
|||
public virtual async Task<IActionResult> 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<IActionResult> 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<bool> 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<List<ExternalLoginProviderModel>> GetExternalProviders() |
|||
{ |
|||
var schemes = await SchemeProvider.GetAllSchemesAsync(); |
|||
var externalProviders = await ExternalProviderService.GetAllAsync(); |
|||
|
|||
var externalProviderModels = new List<ExternalLoginProviderModel>(); |
|||
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<ExternalLoginProviderModel> 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<IActionResult> 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; } |
|||
} |
|||
} |
|||
|
|||
Loading…
Reference in new issue