网站首页 网站源码
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, "撤销用户授权和令牌失败");
}
}
}
上述代码是一个 ASP.NET Core MVC 控制器,名为 AccountController,主要负责用户账户的认证和管理功能。以下是代码的主要功能和结构的详细解释:
AccountController 处理与用户账户相关的操作,如登录、登出、修改密码等。IAccountLoginHistoryService、IUserTwoFactorService、IPinCodeValidator 等。Index 方法:
returnUrl 参数,用于登录后重定向。Login 方法:
pinCodeValidator 验证用户输入的 PIN 码。signInManager 验证用户名和密码。Logout 方法:HttpContext.SignOutAsync() 注销用户。returnUrl 参数重定向用户。ChangePassword 方法:
HandleChangePassword 方法:
accountService 修改密码。RevokeAllUserAuthorizationsAndTokensAsync 方法:OpenIddictAuthorizationManager 和 IOpenIddictTokenManager 来执行撤销操作。TempData 存储临时消息,以便在重定向后显示给用户。ModelState 来验证输入模型的有效性。这个控制器实现了一个基本的用户账户管理系统,支持用户登录、登出、修改密码和双因素验证等功能。它通过使用 ASP.NET Core 的身份验证和授权机制,确保用户的安全性和数据的完整性。
