feat: 管理后台登录增加手机号校验

This commit is contained in:
2026-01-30 07:09:35 +00:00
parent f4ac933716
commit 8dff802248
5 changed files with 39 additions and 57 deletions

View File

@@ -23,7 +23,7 @@ namespace TakeoutSaaS.AdminApi.Controllers;
public sealed class AuthController(IAdminAuthService authService, IMediator mediator) : BaseApiController
{
/// <summary>
/// 登录获取 Token
/// 账号名+手机号登录获取 Token
/// </summary>
/// <param name="request">登录请求。</param>
/// <param name="cancellationToken">取消标记。</param>
@@ -37,22 +37,6 @@ public sealed class AuthController(IAdminAuthService authService, IMediator medi
return ApiResponse<TokenResponse>.Ok(response);
}
/// <summary>
/// 免租户号登录(仅账号+密码)。
/// </summary>
/// <param name="request">登录请求。</param>
/// <param name="cancellationToken">取消标记。</param>
/// <returns>包含访问令牌与刷新令牌的响应。</returns>
/// <remarks>用于前端简化登录,无需额外传递租户号。</remarks>
[HttpPost("login/simple")]
[AllowAnonymous]
[ProducesResponseType(typeof(ApiResponse<TokenResponse>), StatusCodes.Status200OK)]
public async Task<ApiResponse<TokenResponse>> LoginSimple([FromBody] AdminLoginRequest request, CancellationToken cancellationToken)
{
var response = await authService.LoginSimpleAsync(request, cancellationToken);
return ApiResponse<TokenResponse>.Ok(response);
}
/// <summary>
/// 刷新 Token
/// </summary>

View File

@@ -12,11 +12,6 @@ public interface IAdminAuthService
/// </summary>
Task<TokenResponse> LoginAsync(AdminLoginRequest request, CancellationToken cancellationToken = default);
/// <summary>
/// 简化登录与标准登录一致Admin Portal
/// </summary>
Task<TokenResponse> LoginSimpleAsync(AdminLoginRequest request, CancellationToken cancellationToken = default);
/// <summary>
/// 刷新 Token。
/// </summary>

View File

@@ -5,19 +5,26 @@ namespace TakeoutSaaS.Application.Identity.Contracts;
/// <summary>
/// 管理后台登录请求。
/// </summary>
public sealed class AdminLoginRequest
public sealed record AdminLoginRequest
{
/// <summary>
/// 账号。
/// 账号
/// </summary>
[Required]
[MaxLength(64)]
public string Account { get; set; } = string.Empty;
public string AccountName { get; init; } = string.Empty;
/// <summary>
/// 手机号。
/// </summary>
[Required]
[MaxLength(32)]
public string Phone { get; init; } = string.Empty;
/// <summary>
/// 密码。
/// </summary>
[Required]
[MaxLength(128)]
public string Password { get; set; } = string.Empty;
public string Password { get; init; } = string.Empty;
}

View File

@@ -32,11 +32,28 @@ public sealed class AdminAuthService(
/// <exception cref="BusinessException">账号或密码错误时抛出</exception>
public async Task<TokenResponse> LoginAsync(AdminLoginRequest request, CancellationToken cancellationToken = default)
{
// 1. 根据账号查找用户
var user = await userRepository.FindByAccountAsync(PortalType.Admin, null, request.Account, cancellationToken)
?? throw new BusinessException(ErrorCodes.Unauthorized, "账号或密码错误");
// 1. 标准化输入
var accountName = request.AccountName?.Trim() ?? string.Empty;
var phone = request.Phone?.Trim() ?? string.Empty;
var password = request.Password;
// 2. 校验账号状态
// 2. (空行后) 参数校验
if (string.IsNullOrWhiteSpace(accountName) || string.IsNullOrWhiteSpace(phone) || string.IsNullOrWhiteSpace(password))
{
throw new BusinessException(ErrorCodes.BadRequest, "账号名、手机号与密码不能为空");
}
// 3. (空行后) 根据账号查找用户
var user = await userRepository.FindByAccountAsync(PortalType.Admin, null, accountName, cancellationToken)
?? throw new BusinessException(ErrorCodes.Unauthorized, "账号名、手机号或密码错误");
// 4. (空行后) 校验手机号匹配
if (!string.Equals(user.Phone?.Trim(), phone, StringComparison.Ordinal))
{
throw new BusinessException(ErrorCodes.Unauthorized, "账号名、手机号或密码错误");
}
// 5. (空行后) 校验账号状态
var now = DateTime.UtcNow;
if (user.Status == IdentityUserStatus.Disabled)
{
@@ -58,43 +75,22 @@ public sealed class AdminAuthService(
throw new BusinessException(ErrorCodes.Forbidden, "账号需要重置密码,请通过重置链接设置新密码");
}
// 3. 验证密码(使用 ASP.NET Core Identity 的密码哈希器)
var result = passwordHasher.VerifyHashedPassword(user, user.PasswordHash, request.Password);
// 6. (空行后) 验证密码(使用 ASP.NET Core Identity 的密码哈希器)
var result = passwordHasher.VerifyHashedPassword(user, user.PasswordHash, password);
if (result == PasswordVerificationResult.Failed)
{
await IncreaseFailedLoginAsync(user.Id, now, cancellationToken);
throw new BusinessException(ErrorCodes.Unauthorized, "账号或密码错误");
throw new BusinessException(ErrorCodes.Unauthorized, "账号名、手机号或密码错误");
}
// 4. 更新登录成功状态
// 7. (空行后) 更新登录成功状态
await UpdateLoginSuccessAsync(user.Id, now, cancellationToken);
// 5. 构建用户档案并生成令牌
// 8. (空行后) 构建用户档案并生成令牌
var profile = await BuildProfileAsync(user, cancellationToken);
return await jwtTokenService.CreateTokensAsync(profile, false, cancellationToken);
}
/// <summary>
/// 简化登录与标准登录一致Admin Portal
/// </summary>
/// <param name="request">登录请求</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>令牌响应</returns>
public async Task<TokenResponse> LoginSimpleAsync(AdminLoginRequest request, CancellationToken cancellationToken = default)
{
// 1. 参数校验
if (string.IsNullOrWhiteSpace(request.Account))
{
throw new BusinessException(ErrorCodes.BadRequest, "账号不能为空");
}
// 2. (空行后) 标准化账号
request.Account = request.Account.Trim();
// 3. (空行后) 走标准登录逻辑Admin Portal
return await LoginAsync(request, cancellationToken);
}
/// <summary>
/// 刷新访问令牌:使用刷新令牌获取新的访问令牌和刷新令牌。
/// </summary>