GraphQL应用中如何让鉴权优先于数据注解验证?
GraphQL中让鉴权优先于DataAnnotations验证的解决方案
问题场景
在使用DataAnnotatedModelValidations包的GraphQL应用中,当前执行Mutation时数据验证优先级高于鉴权。当用户无所需权限且输入无效时,预期返回未授权错误,但实际先返回验证错误;仅当输入合法时才返回未授权错误。需要实现无权限时,无论输入是否错误,API都返回“用户未授权...”错误的效果。
相关代码配置
Program.cs
builder.Services.AddAuthorization(); builder.Services .AddGraphQLServer() .AddDataAnnotationsValidator() .AddMutationType<Mutation>() .AddMutation<UserMutations>() .AddDefaultTransactionScopeHandler(); var app = builder.Build(); app.UseAuthentication(); app.UseAuthorization(); app.MapGraphQL("/"); app.Run();
UserMutations.cs
[ExtendObjectType(typeof(Mutation))] public class UserMutations { [Authorize(Roles ="Admin")] public async Task<User> CreateUserAsync(CreateUserInput input, [Service] UserService service ){ await service.CreateUserAsync(input); } }
CreateUserInput.cs
using System.ComponentModel.DataAnnotations; public class CreateUserInput{ [MaxLength(8)] public string UserName{get;set;} [MaxLength(15)] public string FirstName{get;set;} [MaxLength(15)] public string LastName{get;set;} }
解决方案
要让鉴权流程优先于数据验证,可通过自定义验证器调整执行顺序,确保鉴权逻辑先触发:
方法:自定义优先鉴权的验证器
- 修改Program.cs配置
移除默认的AddDataAnnotationsValidator,改用自定义验证过滤器:
builder.Services.AddAuthorization(); builder.Services .AddGraphQLServer() .AddMutationType<Mutation>() .AddMutation<UserMutations>() .AddDefaultTransactionScopeHandler() // 替换为自定义验证配置 .AddValidation(options => { options.Validator = new PrioritizedAuthorizationValidator(); }); var app = builder.Build(); app.UseAuthentication(); app.UseAuthorization(); app.MapGraphQL("/"); app.Run();
- 实现PrioritizedAuthorizationValidator
这个验证器会先检查字段的鉴权属性,失败直接返回未授权错误,通过后再执行数据验证:
using System.ComponentModel.DataAnnotations; using HotChocolate.Validation; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using System.Linq; using System.Threading.Tasks; public class PrioritizedAuthorizationValidator : IInputValidator { private readonly IAuthorizationService _authorizationService; private readonly IHttpContextAccessor _httpContextAccessor; private readonly DataAnnotationsInputValidator _innerValidator; public PrioritizedAuthorizationValidator(IAuthorizationService authorizationService, IHttpContextAccessor httpContextAccessor) { _authorizationService = authorizationService; _httpContextAccessor = httpContextAccessor; _innerValidator = new DataAnnotationsInputValidator(); } public async ValueTask<IReadOnlyList<IValidationError>> ValidateAsync(InputValidationContext context) { // 提取当前字段的Authorize属性 var authorizeAttributes = context.Field.Member.GetCustomAttributes(typeof(AuthorizeAttribute), true) .Cast<AuthorizeAttribute>(); foreach (var authAttr in authorizeAttributes) { var httpContext = _httpContextAccessor.HttpContext; if (httpContext == null) { return new[] { new ValidationError("无法获取请求上下文,鉴权失败") }; } // 构建授权策略并执行检查 var policy = string.IsNullOrEmpty(authAttr.Policy) ? new AuthorizationPolicyBuilder().RequireRoles(authAttr.Roles.Split(',')).Build() : new AuthorizationPolicyBuilder().AddRequirements().Build(); var authResult = await _authorizationService.AuthorizeAsync(httpContext.User, null, policy); if (!authResult.Succeeded) { // 鉴权失败直接返回错误,跳过数据验证 return new[] { new ValidationError("用户未授权,无法执行此操作") }; } } // 鉴权通过后执行数据验证 return await _innerValidator.ValidateAsync(context); } }
关键原理
GraphQL的验证器默认按注册顺序执行,默认的DataAnnotationsValidator会先于鉴权逻辑触发。通过自定义验证器,我们在数据验证前插入鉴权检查,一旦鉴权失败就直接返回错误,跳过后续验证步骤,实现权限优先的需求。
内容的提问来源于stack exchange,提问作者Secler developer
相关产品推荐
相关产品推荐

