using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using System.Text; using MiAssessment.Core.Interfaces; using MiAssessment.Model.Entities; using MiAssessment.Model.Models.Auth; using Microsoft.Extensions.Logging; using Microsoft.IdentityModel.Tokens; namespace MiAssessment.Core.Services; /// /// JWT服务实现 /// public class JwtService : IJwtService { private readonly JwtSettings _jwtSettings; private readonly ILogger _logger; public JwtService(JwtSettings jwtSettings, ILogger logger) { _jwtSettings = jwtSettings ?? throw new ArgumentNullException(nameof(jwtSettings)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); } /// /// 生成JWT Token /// public string GenerateToken(User user) { if (user == null) throw new ArgumentNullException(nameof(user)); var key = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(_jwtSettings.Secret)); var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); var claims = new List { new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()), new Claim(ClaimTypes.Name, user.Nickname ?? string.Empty), new Claim("uid", user.Uid ?? string.Empty) }; var token = new JwtSecurityToken( issuer: _jwtSettings.Issuer, audience: _jwtSettings.Audience, claims: claims, expires: DateTime.UtcNow.AddMinutes(_jwtSettings.ExpirationMinutes), signingCredentials: credentials ); var tokenHandler = new JwtSecurityTokenHandler(); var tokenString = tokenHandler.WriteToken(token); _logger.LogInformation("Generated JWT token for user {UserId}", user.Id); return tokenString; } /// /// 验证JWT Token /// public ClaimsPrincipal? ValidateToken(string token) { if (string.IsNullOrWhiteSpace(token)) { _logger.LogWarning("Token validation failed: token is null or empty"); return null; } try { var key = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(_jwtSettings.Secret)); var tokenHandler = new JwtSecurityTokenHandler(); var principal = tokenHandler.ValidateToken(token, new TokenValidationParameters { ValidateIssuerSigningKey = true, IssuerSigningKey = key, ValidateIssuer = true, ValidIssuer = _jwtSettings.Issuer, ValidateAudience = true, ValidAudience = _jwtSettings.Audience, ValidateLifetime = true, ClockSkew = TimeSpan.Zero }, out SecurityToken validatedToken); _logger.LogInformation("Token validated successfully"); return principal; } catch (SecurityTokenExpiredException ex) { _logger.LogWarning("Token validation failed: token expired - {Message}", ex.Message); return null; } catch (SecurityTokenInvalidSignatureException ex) { _logger.LogWarning("Token validation failed: invalid signature - {Message}", ex.Message); return null; } catch (Exception ex) { _logger.LogWarning("Token validation failed: {Message}", ex.Message); return null; } } /// /// 从Token中提取用户ID /// public long? GetUserIdFromToken(string token) { if (string.IsNullOrWhiteSpace(token)) return null; var principal = ValidateToken(token); if (principal == null) return null; var userIdClaim = principal.FindFirst(ClaimTypes.NameIdentifier); if (userIdClaim == null || !long.TryParse(userIdClaim.Value, out var userId)) { _logger.LogWarning("Failed to extract user ID from token"); return null; } return userId; } }