Files
emall-api/FutureMailAPI/Filters/OAuthAuthenticationFilter.cs
2025-10-16 15:21:52 +08:00

138 lines
6.1 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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<OAuthAuthenticationFilter> _logger;
private readonly IConfiguration _configuration;
private readonly FutureMailDbContext _context;
public OAuthAuthenticationFilter(IOAuthService oauthService, ILogger<OAuthAuthenticationFilter> 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<Microsoft.AspNetCore.Authorization.AllowAnonymousAttribute>() != 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 = "令牌验证失败" });
}
}
}
}