using Fengling.Backend.Domain.AggregatesModel.AdminAggregate;
using Fengling.Backend.Infrastructure.Repositories;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
namespace Fengling.Backend.Web.Application.Commands.AdminAuth;
///
/// 管理员登录命令
///
public record AdminLoginCommand(string Username, string Password) : ICommand;
///
/// 管理员登录响应
///
public record AdminLoginResponse(AdminId AdminId, string Username, string Token, DateTime ExpiresAt);
///
/// 管理员登录命令验证器
///
public class AdminLoginCommandValidator : AbstractValidator
{
public AdminLoginCommandValidator()
{
RuleFor(x => x.Username)
.NotEmpty().WithMessage("用户名不能为空")
.MaximumLength(50).WithMessage("用户名长度不能超过50个字符");
RuleFor(x => x.Password)
.NotEmpty().WithMessage("密码不能为空");
}
}
///
/// 管理员登录命令处理器
///
public class AdminLoginCommandHandler(
IAdminRepository adminRepository,
IConfiguration configuration)
: ICommandHandler
{
public async Task Handle(AdminLoginCommand command, CancellationToken cancellationToken)
{
// 1. 通过用户名查询管理员
var admin = await adminRepository.GetByUsernameAsync(command.Username, cancellationToken);
if (admin is null)
{
throw new KnownException("用户名或密码错误");
}
// 2. 检查状态
if (admin.Status == AdminStatus.Disabled)
{
throw new KnownException("账号已被禁用");
}
// 3. 验证密码
if (!admin.VerifyPassword(command.Password))
{
throw new KnownException("用户名或密码错误");
}
// 4. 记录登录时间
admin.RecordLogin();
// 5. 生成 JWT Token
var token = GenerateJwtToken(admin.Id, admin.Username);
var expiresAt = DateTime.UtcNow.AddHours(24);
return new AdminLoginResponse(admin.Id, admin.Username, token, expiresAt);
}
private string GenerateJwtToken(AdminId adminId, string username)
{
var appConfig = configuration.GetSection("AppConfiguration").Get()
?? new Utils.AppConfiguration
{
JwtIssuer = "FenglingBackend",
JwtAudience = "FenglingBackend",
Secret = "YourVerySecretKeyForJwtTokenGeneration12345!"
};
var claims = new[]
{
new Claim(ClaimTypes.NameIdentifier, adminId.ToString()),
new Claim(ClaimTypes.Name, username),
new Claim(ClaimTypes.Role, "Admin"),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
};
// 使用对称密钥
var secret = appConfig.Secret.Length >= 32 ? appConfig.Secret : "YourVerySecretKeyForJwtTokenGeneration12345!";
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secret));
var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken(
issuer: appConfig.JwtIssuer,
audience: appConfig.JwtAudience,
claims: claims,
expires: DateTime.UtcNow.AddHours(24),
signingCredentials: credentials
);
return new JwtSecurityTokenHandler().WriteToken(token);
}
}