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 = "令牌验证失败" });
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								    }
							 | 
						|||
| 
								 | 
							
								}
							 |