using FutureMailAPI.Services; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using Microsoft.IdentityModel.Tokens; using Microsoft.Extensions.Configuration; using System.Text; using FutureMailAPI.Data; namespace FutureMailAPI.Filters { public class OAuthAuthenticationFilter : IAsyncActionFilter { private readonly IOAuthService _oauthService; private readonly ILogger _logger; private readonly IConfiguration _configuration; private readonly FutureMailDbContext _context; public OAuthAuthenticationFilter(IOAuthService oauthService, ILogger logger, IConfiguration configuration, FutureMailDbContext context) { _oauthService = oauthService; _logger = logger; _configuration = configuration; _context = context; } public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) { // 跳过带有AllowAnonymous特性的操作 var endpoint = context.HttpContext.GetEndpoint(); if (endpoint?.Metadata?.GetMetadata() != null) { await next(); return; } // 从Authorization头获取令牌 var authHeader = context.HttpContext.Request.Headers.Authorization.FirstOrDefault(); if (string.IsNullOrEmpty(authHeader) || !authHeader.StartsWith("Bearer ")) { context.Result = new UnauthorizedObjectResult(new { error = "缺少授权令牌" }); return; } var token = authHeader.Substring("Bearer ".Length).Trim(); _logger.LogInformation("正在验证令牌: {Token}", token.Substring(0, Math.Min(50, token.Length)) + "..."); try { // 首先尝试验证JWT令牌 var tokenHandler = new JwtSecurityTokenHandler(); var jwtSettings = _configuration.GetSection("Jwt"); var key = Encoding.ASCII.GetBytes(jwtSettings["Key"] ?? throw new InvalidOperationException("JWT密钥未配置")); var validationParameters = new TokenValidationParameters { ValidateIssuerSigningKey = true, IssuerSigningKey = new SymmetricSecurityKey(key), ValidateIssuer = true, ValidIssuer = jwtSettings["Issuer"], ValidateAudience = true, ValidAudience = jwtSettings["Audience"], ValidateLifetime = true, ClockSkew = TimeSpan.Zero }; // 验证JWT令牌 var principal = tokenHandler.ValidateToken(token, validationParameters, out SecurityToken validatedToken); // 检查令牌类型 var tokenType = principal.FindFirst("token_type")?.Value; _logger.LogInformation("令牌类型: {TokenType}", tokenType ?? "普通用户令牌"); if (tokenType == "oauth") { // OAuth令牌,检查数据库中是否存在 var oauthToken = await _oauthService.GetTokenAsync(token); if (oauthToken == null) { _logger.LogWarning("OAuth令牌不存在或已过期"); context.Result = new UnauthorizedObjectResult(new { error = "OAuth令牌不存在或已过期" }); return; } } else { // 普通用户令牌,检查用户表中是否有此用户 var userId = principal.FindFirst(ClaimTypes.NameIdentifier)?.Value; _logger.LogInformation("用户ID: {UserId}", userId); if (string.IsNullOrEmpty(userId) || !int.TryParse(userId, out int uid)) { _logger.LogWarning("令牌中未包含有效的用户ID"); context.Result = new UnauthorizedObjectResult(new { error = "令牌无效" }); return; } // 验证用户是否存在 var user = await _context.Users.FindAsync(uid); if (user == null) { _logger.LogWarning("用户ID {UserId} 不存在", uid); context.Result = new UnauthorizedObjectResult(new { error = "用户不存在" }); return; } _logger.LogInformation("用户验证成功: {UserId}", uid); } // 设置用户信息 context.HttpContext.User = principal; await next(); } catch (SecurityTokenExpiredException) { _logger.LogWarning("令牌已过期"); context.Result = new UnauthorizedObjectResult(new { error = "令牌已过期" }); } catch (SecurityTokenInvalidSignatureException) { _logger.LogWarning("令牌签名无效"); context.Result = new UnauthorizedObjectResult(new { error = "令牌签名无效" }); } catch (SecurityTokenException ex) { _logger.LogWarning(ex, "令牌验证失败"); context.Result = new UnauthorizedObjectResult(new { error = "令牌验证失败" }); } catch (Exception ex) { _logger.LogError(ex, "OAuth认证时发生错误"); context.Result = new UnauthorizedObjectResult(new { error = "令牌验证失败" }); } } } }