网站首页 网站源码
website
站点相关全部源代码,隐藏了一些关于服务器的信息
using System.ComponentModel.DataAnnotations;
using Dpz.Core.Auth.Models;
using Dpz.Core.Auth.Service;
using Dpz.Core.Public.Entity.Auth;
using Dpz.Core.Public.ViewModel;
using Dpz.Core.Service.RepositoryService;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using OpenIddict.Abstractions;
using OpenIddict.Core;

namespace Dpz.Core.Auth.Controllers;

public class AccountController(ILogger<AccountController> logger) : Controller
{
    [HttpGet("~/sign-in")]
    public IActionResult Index(string? returnUrl = null)
    {
        // 如果用户已经登录,根据 returnUrl 进行重定向
        if (User.Identity?.IsAuthenticated == true)
        {
            // 如果有 returnUrl 且是本地URL,重定向到该URL
            if (!string.IsNullOrWhiteSpace(returnUrl) && Url.IsLocalUrl(returnUrl))
            {
                return Redirect(returnUrl);
            }

            // 否则重定向到默认首页
            return RedirectToAction("Index", "Home");
        }

        return View(model: returnUrl);
    }

    [ValidateAntiForgeryToken]
    [HttpPost("~/sign-in")]
    public async Task<IActionResult> Login(
        [FromServices] IAccountLoginHistoryService accountLoginHistoryService,
        [FromServices] IUserTwoFactorService userTwoFactorService,
        [FromServices] IPinCodeValidator pinCodeValidator,
        [FromServices] SignInManager<VmUserInfo> signInManager,
        [Required] string account,
        [Required] string password,
        string? returnUrl,
        string? pinCode = null,
        bool remember = false
    )
    {
        if (string.IsNullOrWhiteSpace(returnUrl))
        {
            returnUrl = Url.Action("Index", "Home");
        }

        if (await accountLoginHistoryService.LockedAsync(account))
        {
            await accountLoginHistoryService.AccountLockedAsync(account);
            TempData["Msg"] = "短时间内多次登录失败,账户已被锁定!";
            return RedirectToAction("Index", new { returnUrl });
        }

        // 双因素验证
        var pinValidationResult = await pinCodeValidator.ValidateForAuthAsync(account, pinCode);
        if (!pinValidationResult.Success)
        {
            TempData["Msg"] = pinValidationResult.Message;
            await accountLoginHistoryService.PinCodeErrorAsync(account);
            return RedirectToAction("Index", new { returnUrl });
        }

        var result = await signInManager.PasswordSignInAsync(account, password, remember, false);
        if (result.Succeeded)
        {
            await accountLoginHistoryService.SuccessAsync(account);

            // 检查用户是否已绑定双因素验证,如果未绑定则跳转到绑定页面
            var (_, isBind) = await userTwoFactorService.GetKeyAsync(account);
            if (!isBind)
            {
                return RedirectToAction("BindTwoFactor", "TwoFactor", new { returnUrl });
            }
            return Redirect(returnUrl!);
        }

        // 检查是否是因为账户被禁用导致的登录失败
        if (HttpContext.HasAccountDisabledFlag())
        {
            await accountLoginHistoryService.AccountDisabledAsync(account);
            TempData["Msg"] = "您的账户已被禁用,无法登录!如有疑问,请联系管理员。";
            return RedirectToAction("Index", new { returnUrl });
        }

        // 密码错误或其他原因
        await accountLoginHistoryService.AccountOrPasswordErrorAsync(account);
        TempData["Msg"] = "用户名或密码错误!";
        return RedirectToAction("Index", new { returnUrl });
    }

    [Authorize, Route("~/sign-out")]
    public async Task<IActionResult> Logout(string? returnUrl)
    {
        await HttpContext.SignOutAsync();
        return string.IsNullOrWhiteSpace(returnUrl) ? Redirect("/") : Redirect(returnUrl);
    }

    [Authorize, HttpGet, Route("change-password.html")]
    public IActionResult ChangePassword()
    {
        return View();
    }

    [Authorize, ValidateAntiForgeryToken, HttpPost]
    public async Task<IActionResult> HandleChangePassword(
        [FromServices] IAccountService accountService,
        [FromServices] IPinCodeValidator pinCodeValidator,
        [FromServices] IAccountLoginHistoryService accountLoginHistoryService,
        [FromServices] OpenIddictAuthorizationManager<DpzAuthorization> authorizationManager,
        [FromServices] IOpenIddictTokenManager tokenManager,
        ChangePasswordModel model
    )
    {
        // 验证模型状态
        if (!ModelState.IsValid)
        {
            var errors = ModelState
                .Where(x => x.Value?.Errors.Count > 0)
                .SelectMany(x => x.Value!.Errors)
                .Select(x => x.ErrorMessage)
                .ToList();

            TempData["Msg"] = string.Join(";", errors);
            return View("ChangePassword", model);
        }

        var account = User.NameIdentifier;
        if (string.IsNullOrWhiteSpace(account))
        {
            TempData["Msg"] = "用户身份验证失败,请重新登录";
            return RedirectToAction("Index", "Account");
        }

        // 双因素验证
        var pinValidationResult = await pinCodeValidator.ValidateForAuthAsync(
            account,
            model.PinCode
        );
        if (!pinValidationResult.Success)
        {
            TempData["Msg"] = pinValidationResult.Message;
            await accountLoginHistoryService.PinCodeErrorAsync(account);
            return View("ChangePassword", model);
        }

        try
        {
            var result = await accountService.ChangePasswordAsync(
                account,
                model.Password!,
                model.NewPassword!
            );

            if (!result.Success)
            {
                TempData["Msg"] = result.Message ?? "密码修改失败,请稍后重试";
                return View("ChangePassword", model);
            }

            // 密码修改成功,撤销该账号所有授权和令牌
            await RevokeAllUserAuthorizationsAndTokensAsync(
                authorizationManager,
                tokenManager,
                account
            );

            // 登出当前用户,要求重新登录
            await HttpContext.SignOutAsync();

            // 将成功消息传递到登录页面
            TempData["Msg"] = "密码修改成功!为了您的账户安全,已撤销所有授权,请重新登录";
            return RedirectToAction("Index", "Account");
        }
        catch (Exception e)
        {
            TempData["Msg"] = "系统错误,请稍后重试";
            logger.LogError(e, "密码修改失败");
            return View("ChangePassword", model);
        }
    }

    /// <summary>
    /// 撤销用户的所有授权和令牌
    /// </summary>
    /// <param name="authorizationManager">授权管理器</param>
    /// <param name="tokenManager">令牌管理器</param>
    /// <param name="userId">用户ID</param>
    private async Task RevokeAllUserAuthorizationsAndTokensAsync(
        OpenIddictAuthorizationManager<DpzAuthorization> authorizationManager,
        IOpenIddictTokenManager tokenManager,
        string userId
    )
    {
        try
        {
            // 撤销用户的所有令牌
            // 参数说明:subject(用户ID), client(null表示所有客户端), status(null表示所有状态), type(null表示所有类型)
            await tokenManager.RevokeAsync(userId, null, null, null);

            // 撤销用户的所有授权
            // 参数说明:subject(用户ID), client(null表示所有客户端), status(null表示所有状态), type(null表示所有类型)
            await authorizationManager.RevokeAsync(userId, null, null, null);
        }
        catch (Exception e)
        {
            logger.LogError(e, "撤销用户授权和令牌失败");
        }
    }
}
⚠⚠    以下内容为AI分析的结果,请根据实际情况进行判断。

上述代码是一个 ASP.NET Core MVC 控制器,名为 AccountController,主要负责用户账户的认证和管理功能。以下是代码的主要功能和结构的详细解释:

1. 控制器概述

  • AccountController 处理与用户账户相关的操作,如登录、登出、修改密码等。
  • 该控制器使用了依赖注入来获取服务,例如 IAccountLoginHistoryServiceIUserTwoFactorServiceIPinCodeValidator 等。

2. 登录功能

  • Index 方法:

    • 处理 GET 请求,返回登录页面。
    • 可接受一个可选的 returnUrl 参数,用于登录后重定向。
  • Login 方法:

    • 处理 POST 请求,执行用户登录逻辑。
    • 首先检查账户是否被锁定,如果是,则返回锁定消息。
    • 进行双因素验证,使用 pinCodeValidator 验证用户输入的 PIN 码。
    • 使用 signInManager 验证用户名和密码。
    • 如果登录成功,检查用户是否绑定了双因素验证,如果未绑定,则重定向到绑定页面。
    • 如果登录失败,记录登录失败信息并返回错误消息。

3. 登出功能

  • Logout 方法:
    • 处理用户登出请求,使用 HttpContext.SignOutAsync() 注销用户。
    • 根据 returnUrl 参数重定向用户。

4. 修改密码功能

  • ChangePassword 方法:

    • 返回修改密码页面。
  • HandleChangePassword 方法:

    • 处理修改密码的 POST 请求。
    • 验证模型状态,如果无效则返回错误信息。
    • 验证用户身份,如果身份验证失败则重定向到登录页面。
    • 进行双因素验证。
    • 调用 accountService 修改密码。
    • 如果修改成功,撤销用户的所有授权和令牌,并要求用户重新登录。
    • 如果发生异常,记录错误并返回错误消息。

5. 撤销用户授权和令牌

  • RevokeAllUserAuthorizationsAndTokensAsync 方法:
    • 撤销用户的所有授权和令牌,确保在密码修改后,用户的旧授权和令牌不再有效。
    • 使用 OpenIddictAuthorizationManagerIOpenIddictTokenManager 来执行撤销操作。

6. 其他功能

  • 使用 TempData 存储临时消息,以便在重定向后显示给用户。
  • 使用 ModelState 来验证输入模型的有效性。
  • 通过依赖注入获取服务,保持代码的可测试性和可维护性。

总结

这个控制器实现了一个基本的用户账户管理系统,支持用户登录、登出、修改密码和双因素验证等功能。它通过使用 ASP.NET Core 的身份验证和授权机制,确保用户的安全性和数据的完整性。

loading