138 lines
6.1 KiB
C#
138 lines
6.1 KiB
C#
|
|
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 = "令牌验证失败" });
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|