ASP.NET Core MVC 身份登录管理器

问题描述 投票:0回答:0

所以我有 API 和 MVC 项目。我的目标是执行以下操作:当用户从 API 项目注册时,他应该能够从 MVC 项目进行身份验证,反之亦然。几乎一切正常,但有一个问题。在 MVC Identity 中我无法进行实际登录,我的意思是我不能使用 SignInManager.SignInAsync() 方法来 SignInUser 然后使用 _LoginPartial 视图检查用户是否登录。

由于身份密码哈希和我的哈希之间的差异,我决定创建 CustomSignInManager 类来检查密码哈希是否相同等。

CustomSignInManager 类:

public class CustomSignInManager : SignInManager<User>
{
    private readonly IUserAuthentication _userAuthentication;
    private readonly Encryptor<User> _encryptor;

    public CustomSignInManager(UserManager<User> userManager,
        IHttpContextAccessor contextAccessor,
        IUserClaimsPrincipalFactory<User> claimsFactory,
        IOptions<IdentityOptions> optionsAccessor,
        ILogger<SignInManager<User>> logger,
        IAuthenticationSchemeProvider schemes,
        IUserConfirmation<User> confirmation,
        IUserAuthentication userAuthentication,
        Encryptor<User> encryptor)
        : base(userManager, contextAccessor, claimsFactory, optionsAccessor, logger, schemes, confirmation)
    {
        _userAuthentication = userAuthentication;
        _encryptor = encryptor;
    }

    public async Task<AuthenticationResponse> PasswordSignInAsync(string email, string password)
    {
        var user = await UserManager.FindByEmailAsync(email);

        if (user == null)
        {
            throw new InvalidCredentialsException(ErrorMessages.InvalidCredentialsException);
        }

        if (_encryptor.VerifyHashedPassword(user, user.PasswordHash, password) != PasswordVerificationResult.Success)
        {
            throw new InvalidCredentialsException(ErrorMessages.InvalidCredentialsException);
        }

        var request = new AuthenticationRequest
        {
            Email = email,
            Password = password,
        };

        var response = await _userAuthentication.AuthenticateAsync(request);

        if (response == null)
        {
            throw new InvalidCredentialsException(ErrorMessages.InvalidCredentialsException);
        }

        await SignInAsync(user, isPersistent: false);

        return response;
    }
}

Identity DI 容器注入的扩展

public static class IdentityExtension
{
    public static IServiceCollection AddIdentityUser(this IServiceCollection services)
    {
        services.AddDefaultIdentity<User>()
            .AddEntityFrameworkStores<EventsDemoDbContext>();

        services.AddScoped<SignInManager<User>, CustomSignInManager>();
        services.AddScoped<CustomSignInManager>();

        return services;
    }
}

登录剃须刀页面:

 public class LoginModel : PageModel
    {
        private readonly CustomSignInManager _signInManager;
        private readonly ILogger<LoginModel> _logger;

        public LoginModel(CustomSignInManager signInManager, ILogger<LoginModel> logger)
        {
            _signInManager = signInManager;
            _logger = logger;
        }

        /// <summary>
        ///     This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
        ///     directly from your code. This API may change or be removed in future releases.
        /// </summary>
        [BindProperty]
        public InputModel Input { get; set; }

        /// <summary>
        ///     This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
        ///     directly from your code. This API may change or be removed in future releases.
        /// </summary>
        public IList<AuthenticationScheme> ExternalLogins { get; set; }

        /// <summary>
        ///     This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
        ///     directly from your code. This API may change or be removed in future releases.
        /// </summary>
        public string ReturnUrl { get; set; }

        /// <summary>
        ///     This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
        ///     directly from your code. This API may change or be removed in future releases.
        /// </summary>
        [TempData]
        public string ErrorMessage { get; set; }

        /// <summary>
        ///     This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
        ///     directly from your code. This API may change or be removed in future releases.
        /// </summary>
        public class InputModel
        {
            /// <summary>
            ///     This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
            ///     directly from your code. This API may change or be removed in future releases.
            /// </summary>
            [Required]
            [EmailAddress]
            public string Email { get; set; }

            /// <summary>
            ///     This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
            ///     directly from your code. This API may change or be removed in future releases.
            /// </summary>
            [Required]
            [DataType(DataType.Password)]
            public string Password { get; set; }

            /// <summary>
            ///     This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
            ///     directly from your code. This API may change or be removed in future releases.
            /// </summary>
            [Display(Name = "Remember me?")]
            public bool RememberMe { get; set; }
        }

        public async Task OnGetAsync(string returnUrl = null)
        {
            if (!string.IsNullOrEmpty(ErrorMessage))
            {
                ModelState.AddModelError(string.Empty, ErrorMessage);
            }

            returnUrl ??= Url.Content("~/");

            // Clear the existing external cookie to ensure a clean login process
            await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme);

            ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();

            ReturnUrl = returnUrl;
        }

        public async Task<IActionResult> OnPostAsync(string returnUrl = null)
        {
            returnUrl ??= Url.Content("~/");

            ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();

            if (ModelState.IsValid)
            {
                var response = await _signInManager.PasswordSignInAsync(Input.Email, Input.Password);

                if (response != null)
                {
                    CreateAuthenticationCookie(response);

                    _logger.LogInformation("User logged in.");

                    return LocalRedirect(returnUrl);
                }
                else
                {
                    ModelState.AddModelError(string.Empty, "Invalid login attempt.");
                    return Page();
                }
            }

            // If we got this far, something failed, redisplay form
            return Page();
        }

        public void CreateAuthenticationCookie(AuthenticationResponse response)
        {
            var serializedResponse = JsonConvert.SerializeObject(response);

            HttpContext.Response.Cookies.Append("jwt", serializedResponse, new CookieOptions()
            {
                HttpOnly = true,
                Secure = true,
                SameSite = SameSiteMode.Strict,
                Expires = DateTime.UtcNow.AddMinutes(response.AccessTokenExpiresMinutes),
            });

            //var authCookie = Request.Cookies["jwt"];

            //if (authCookie != null)
            //{
            //    var authResponse = JsonConvert.DeserializeObject<AuthenticationResponse>(authCookie);
            //}
        }
    }

这是_LoginPartial视图:

@using EventsDemo.Domain.Users
@using EventsDemo.Web.Services
@using Microsoft.AspNetCore.Identity

@inject CustomSignInManager SignInManager
@inject UserManager<User> UserManager

<ul class="navbar-nav">
@if (SignInManager.IsSignedIn(User))
{
    <li class="nav-item">
        <a id="manage" class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Manage/Index" title="Manage">Hello @UserManager.GetUserName(User)!</a>
    </li>
    <li class="nav-item">
        <form id="logoutForm" class="form-inline" asp-area="Identity" asp-page="/Account/Logout" asp-route-returnUrl="@Url.Action("Index", "User")">
            <button id="logout" type="submit" class="nav-link btn btn-link text-dark">Logout</button>
        </form>
    </li>
}
else
{
    <li class="nav-item">
        <a class="nav-link text-dark" id="register" asp-area="Identity" asp-page="/Account/Register">Register</a>
    </li>
    <li class="nav-item">
        <a class="nav-link text-dark" id="login" asp-area="Identity" asp-page="/Account/Login">Login</a>
    </li>
}
</ul>

问题是这个 if 条件永远不会为真,因为在 PasswordSignInAsync() 方法中,我猜 await SignInAsync() 方法不起作用

if (response == null)
        {
            throw new InvalidCredentialsException(ErrorMessages.InvalidCredentialsException);
        }

        await SignInAsync(user, isPersistent: false);

        return response;

或者在 _LoginPartial 视图中有第二个选项,我正在使用 CustomSignInManager 的不同实例,并且在 Login razor 页面不同。

我该怎么做才能让它发挥作用?除了这个,其他一切都有效。我在注入 CustomSignInManager 期间遗漏了什么吗?甚至我做得对吗?

c# asp.net-core-mvc asp.net-core-identity
© www.soinside.com 2019 - 2024. All rights reserved.