首先參考 abp modules identity 源代碼,在自己的 Web 專案中創建:
Pages/Account/Login.cshtml
Pages/Account/Login.cshtml.cs
先將 Login.cshtml
的源代碼拷貝過來,然後修改 Login.cshtml.cs
文件的代碼如下:
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Diagnostics;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Volo.Abp.Account.Settings;
using Volo.Abp.Account.Web;
using Volo.Abp.Auditing;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Identity;
using Volo.Abp.Identity.AspNetCore;
using Volo.Abp.Settings;
using Volo.Abp.Validation;
using IdentityUser = Volo.Abp.Identity.IdentityUser;
namespace MiaoXin.Web.Pages.Account
{
/// <summary>
/// Volo.Abp.Account.Web.Pages.Account.LoginModel
/// </summary>
public class LoginModel : Volo.Abp.Account.Web.Pages.Account.LoginModel
{
// https://github.com/abpframework/abp/issues/8068#issuecomment-799981650
private readonly IIdentityUserRepository _identityUserRepository;
public LoginModel(
IIdentityUserRepository identityUserRepository,
IAuthenticationSchemeProvider schemeProvider,
IOptions<AbpAccountOptions> accountOptions,
IOptions<IdentityOptions> identityOptions,
IdentityDynamicClaimsPrincipalContributorCache identityDynamicClaimsPrincipalContributorCache
)
: base(schemeProvider, accountOptions, identityOptions, identityDynamicClaimsPrincipalContributorCache)
{
_identityUserRepository = identityUserRepository;
}
[BindProperty]
public new LoginInputModel LoginInput { get; set; }
// https://github.com/abpframework/abp/issues/7978#issuecomment-830715330
public override async Task<IActionResult> OnPostAsync(string action)
{
//return base.OnPostAsync(action);
await CheckLocalLoginAsync();
ValidateModel();
ExternalProviders = await GetExternalProviders();
EnableLocalLogin = await SettingProvider.IsTrueAsync(AccountSettingNames.EnableLocalLogin);
await ReplacePhoneNumberToUsernameOfInputIfNeeds();
await ReplaceEmailToUsernameOfInputIfNeeds();
await IdentityOptions.SetAsync();
var result = await SignInManager.PasswordSignInAsync(
LoginInput.UserNameOrEmailAddressOrPhoneNumber,
LoginInput.Password,
LoginInput.RememberMe,
true
);
await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext()
{
Identity = IdentitySecurityLogIdentityConsts.Identity,
Action = result.ToIdentitySecurityLogAction(),
UserName = LoginInput.UserNameOrEmailAddressOrPhoneNumber
});
if (result.RequiresTwoFactor)
{
return await TwoFactorLoginResultAsync();
}
if (result.IsLockedOut)
{
Alerts.Warning(L["UserLockedOutMessage"]);
return Page();
}
if (result.IsNotAllowed)
{
Alerts.Warning(L["LoginIsNotAllowed"]);
return Page();
}
if (!result.Succeeded)
{
Alerts.Danger(L["InvalidUserNameOrPassword"]);
return Page();
}
//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 FindUserByPhoneNumberAsync(LoginInput.UserNameOrEmailAddressOrPhoneNumber) ??
await UserManager.FindByNameAsync(LoginInput.UserNameOrEmailAddressOrPhoneNumber) ??
await UserManager.FindByEmailAsync(LoginInput.UserNameOrEmailAddressOrPhoneNumber);
Debug.Assert(user != null, nameof(user) + " != null");
// Clear the dynamic claims cache.
await IdentityDynamicClaimsPrincipalContributorCache.ClearAsync(user.Id, user.TenantId);
return await RedirectSafelyAsync(ReturnUrl, ReturnUrlHash);
}
protected override async Task ReplaceEmailToUsernameOfInputIfNeeds()
{
//return base.ReplaceEmailToUsernameOfInputIfNeeds();
if (!ValidationHelper.IsValidEmailAddress(LoginInput.UserNameOrEmailAddressOrPhoneNumber))
{
return;
}
var userByUsername = await UserManager.FindByNameAsync(LoginInput.UserNameOrEmailAddressOrPhoneNumber);
if (userByUsername != null)
{
return;
}
var userByEmail = await UserManager.FindByEmailAsync(LoginInput.UserNameOrEmailAddressOrPhoneNumber);
if (userByEmail == null)
{
return;
}
LoginInput.UserNameOrEmailAddressOrPhoneNumber = userByEmail.UserName;
}
protected async Task ReplacePhoneNumberToUsernameOfInputIfNeeds()
{
// 手機號必須是 11 位數字
if (!IsPhoneNumber(LoginInput.UserNameOrEmailAddressOrPhoneNumber))
{
return;
}
var userByPhoneNumber = await FindUserByPhoneNumberAsync(LoginInput.UserNameOrEmailAddressOrPhoneNumber);
if (userByPhoneNumber == null)
{
return;
}
LoginInput.UserNameOrEmailAddressOrPhoneNumber = userByPhoneNumber.UserName;
}
/// <summary>
/// 根據手機號查找用戶
/// https://stackoverflow.com/a/70344937
/// </summary>
/// <param name="phoneNumber"></param>
/// <returns></returns>
private async Task<IdentityUser?> FindUserByPhoneNumberAsync(string phoneNumber)
{
// https://github.com/abpframework/abp/issues/8068
// UserManager.Users.FirstOrDefaultAsync(x => x.PhoneNumber == LoginInput.UserNameOrEmailAddressOrPhoneNumber);
var identityUserDbSet = await _identityUserRepository.GetDbSetAsync();
return await identityUserDbSet.FirstOrDefaultAsync(x => x.PhoneNumber == LoginInput.UserNameOrEmailAddressOrPhoneNumber);
}
/// <summary>
/// 判斷是否為手機號
/// https://stackoverflow.com/a/29970789
/// </summary>
/// <param name="number"></param>
/// <returns></returns>
private static bool IsPhoneNumber(string number)
{
return Regex.Match(number, @"^1[0-9]{10}$").Success;
}
public new class LoginInputModel
{
[Required]
[DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxEmailLength))]
[DisplayName("用戶名或手機號")]
public string UserNameOrEmailAddressOrPhoneNumber { get; set; }
[Required]
[DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxPasswordLength))]
[DataType(DataType.Password)]
[DisableAuditing]
[DisplayName("密碼")]
public string Password { get; set; }
public bool RememberMe { get; set; }
/// <summary>
/// 圖形驗證碼
/// </summary>
[Required]
[StringLength(4)]
[DisplayName("圖形驗證碼")]
public string Captcha { get; set; }
}
}
}