ASP.NET Core Web API自定义授权策略失败返回500而非401问题
问题场景
在ASP.NET Core中实现自定义AuthorizationPolicy,操作步骤如下:
- 继承
IAuthorizationRequirement创建TokenRequirement类 - 编写对应的
TokenHandler授权处理类 - 在Program.cs中注册授权处理器与自定义策略
- 在控制器上添加
[Authorize(Policy = "Token")]特性
当前问题:Token验证通过时正常返回200状态码及数据,但验证失败时抛出500内部服务器错误,而非预期的401未授权状态码。
自定义授权相关代码
TokenRequirement类
public class TokenRequirement : IAuthorizationRequirement { }
TokenHandler类
public class TokenHandler : AuthorizationHandler<TokenRequirement> { ITokenService tokenService; public TokenHandler(ITokenService tokenService) { this.tokenService = tokenService ?? throw new ArgumentNullException(nameof(tokenService)); } protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, TokenRequirement requirement) { if (context != null) { var httpContext = (DefaultHttpContext)context.Resource; if (httpContext != null) { if (string.IsNullOrEmpty(httpContext.Request.Headers["Authorization"])) context.Fail(); else { string jwtToken = httpContext.Request.Headers["Authorization"].ToString(); JwtSecurityToken token = tokenService.DecryptToken(jwtToken); var claims = token.Claims; if (token.Claims.Where(c => c.Type == "role" && c.Value == @"XXXX Authentication1").Count() > 0) context.Succeed(requirement); else { context.Fail(); } } } else context.Fail(); } return Task.CompletedTask; } }
Program.cs注册代码
builder.Services.AddSingleton<IAuthorizationHandler, FileTransferWebAPI.TokenHandler>();
builder.Services.AddAuthorization(options => { options.AddPolicy("Token", policy => policy.Requirements.Add(new TokenRequirement())); });
控制器特性
[Authorize(Policy ="Token")] public class FileTransferController : ControllerBase
捕获的错误信息
System.ArgumentNullException: String reference not set to an instance of a String. (Parameter 's') at System.Text.Encoding.GetBytes(String s) at System.Text.UTF8Encoding.UTF8EncodingSealed.GetBytes(String s) at Program.<>c__DisplayClass0_0.<<Main>$>b__1(JwtBearerOptions o) in D:\Projects\FileTransferWebAPI\FileTransferWebAPI\Program.cs:line 35 at Microsoft.Extensions.Options.OptionsFactory`1.Create(String name) at Microsoft.Extensions.Options.OptionsCache`1.<>c__3`1.<GetOrAdd>b__3_0(String name, ValueTuple`2 arg) at System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd[TArg](TKey key, Func`3 valueFactory, TArg factoryArgument) at Microsoft.Extensions.Options.OptionsCache`1.GetOrAdd[TArg](String name, Func`3 createOptions, TArg factoryArgument) at Microsoft.Extensions.Options.OptionsMonitor`1.Get(String name) at Microsoft.AspNetCore.Authentication.AuthenticationHandler`1.InitializeAsync(AuthenticationScheme scheme, HttpContext context) at Microsoft.AspNetCore.Authentication.AuthenticationHandlerProvider.GetHandlerAsync(HttpContext context, String authenticationScheme) at Microsoft.AspNetCore.Authentication.AuthenticationService.ChallengeAsync(HttpContext context, String scheme, AuthenticationProperties properties) at Microsoft.AspNetCore.Authorization.Policy.AuthorizationMiddlewareResultHandler.HandleAsync(RequestDelegate next, HttpContext context, AuthorizationPolicy policy, PolicyAuthorizationResult authorizeResult) at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context) at Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext) at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider) at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context) HEADERS ======= Accept: */* Connection: keep-alive Host: 127.0.0.1:6200 User-Agent: PostmanRuntime/7.30.0 Accept-Encoding: gzip, deflate, br Authorization: eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJJZCI6ImFlNjk3OGZmLTY2YTktNGZiYi1hMTAzLThiMWQxNTg4YTc5MCIsInN1YiI6IkJFSUpFUkVMQ1xcQUhaQUgiLCJqdGkiOiI4OWE0M2Y2ZS1mNTNjLTQwZDMtYjBkNS00NzkwYjczNzc1YzgiLCJuYW1laWQiOiJTLTEtNS0yMS04MDk4OTM5NjMtMjkzMjIzMjQ1OC0xMjQzMDA3NzAzLTkzMzIiLCJ1bmlxdWVfbmFtZSI6IkJFSUpFUkVMQ1xcQUhaQUgiLCJyb2xlIjpbIkJFSUpFUkVMQ1xcRG9tYWluIFVzZXJzIiwiRXZlcnlvbmUiLCJCVUlMVElOXFxBZG1pbmlzdHJhdG9ycyIsIkJVSUxUSU5cXFBlcmZvcm1hbmNlIExvZyBVc2VycyIsIkJVSUxUSU5cXFVzZXJzIiwiTlQgQVVUSE9SSVRZXFxORVRXT1JLIiwiTlQgQVVUSE9SSVRZXFxBdXRoZW50aWNhdGVkIFVzZXJzIiwiTlQgQVVUSE9SSVRZXFxUaGlzIE9yZ2FuaXphdGlvbiIsIkJFSUpFUkVMQ1xcQkVfTGFzdHBhc3NfV2VicGx1Z2luX1VzZXJzIiwiQkVJSkVSRUxDXFxHbG9iYWwgSUZTIDMzMjAwIiwiQkVJSkVSRUxDXFxFTUVBX1BBX1ZQTl9GdWxsX0FjY2VzcyIsIkJFSUpFUkVMQ1xcR2xvYmFsIFRlY2huaXF1ZSIsIkJFSUpFUkVMQ1xcRU1FQV9DYXl6dV9FbmR1c2VycyIsIkJFSUpFUkVMQ1xcR2xvYmFsIFN1cHBvcnQiLCJCRUlKRVJFTENcXEdsb2JhbF9FTVNFM19MaWMiLCJCRUlKRVJFTENcXFNjcmF0Y2hfRHJpdmVfQWNjZXNzIiwiQkVJSkVSRUxDXFxHbG9iYWxfU0NDTV9SRF9VU0VSUyIsIkJFSUpFUkVMQ1xcR2xvYmFsX0Nsb3VkX0FjY2VzcyIsIkJFSUpFUkVMQ1xcUEFfVGVzdF9BbGwiLCJCRUlKRVJFTENcXEdsb2JhbF9BbGxfVXNlcnNfQ2xvdWQiLCJCRUlKRVJFTENcXEdsb2JhbCBSRCBBbGwiLCJCRUlKRVJFTENcXERlcHQgNzUgU1cgRGV2ZWxvcG1lbnQiLCJCRUlKRVJFTENcXEJFX0xhc3RwYXNzX1VzZXJzIiwiQkVJSkVSRUxDXFxHbG9iYWwgUkQgU0UiLCJCRUlKRVJFTENcXERlcHQgNzAgSE1JIEV4dHJhIFVzZXJzIiwiQkVJSkVSRUxDXFxHbG9iYWxfTzM2NV9BcHBzX0VYTyIsIkJFSUpFUkVMQ1xcR2xvYmFsIFImRCBITUkiLCJOVCBBVVRIT1JJVFlcXE5UTE0gQXV0aGVudGljYXRpb24iXSwibmJmIjoxNjczMjgwNTEwLCJleHAiOjE2NzMyODA4MTAsImlhdCI6MTY3MzI4MDUxMCwiaXNzIjoicGFuZWwiLCJhdWQiOiJ0cmFuc2ZlckNsaWVudCJ9.ONEXXnlb3dVGs0bcahCtD4EIvZoYMaYDVqV1XYabMRQVmoRMSeEUODtlhsZHOm0Nq2r6c3zBSy_g5j9bTyId-g Postman-Token: d687b92f-0e65-45c8-a1ce-9d7c88d9dfd9
问题原因及解决方法
原因分析
从异常栈可以看出,错误发生在JwtBearerOptions配置的第35行:调用Encoding.GetBytes(String s)时传入了null值。这说明项目中同时配置了JWT Bearer认证,但相关密钥或参数未正确初始化,当授权失败触发Challenge操作时,JWT认证处理器尝试使用null值执行编码操作,最终导致500错误。
解决步骤
修复JWT认证配置
打开Program.cs,找到AddJwtBearer的配置代码,确保所有必填参数(如TokenValidationParameters中的IssuerSigningKey)都已正确赋值,无null值。示例正确配置:builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidateAudience = true, ValidateLifetime = true, ValidateIssuerSigningKey = true, ValidIssuer = builder.Configuration["Jwt:Issuer"], ValidAudience = builder.Configuration["Jwt:Audience"], IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"])) }; });确保配置文件中存在
Jwt:Key、Jwt:Issuer、Jwt:Audience的有效键值对。自定义授权失败处理逻辑
如果不需要JWT Bearer认证,可直接移除相关配置;或者自定义授权结果处理器,跳过默认Challenge流程,直接返回401:builder.Services.AddSingleton<IAuthorizationMiddlewareResultHandler, CustomAuthorizationMiddlewareResultHandler>(); public class CustomAuthorizationMiddlewareResultHandler : IAuthorizationMiddlewareResultHandler { public async Task HandleAsync(RequestDelegate next, HttpContext context, AuthorizationPolicy policy, PolicyAuthorizationResult authorizeResult) { if (!authorizeResult.Succeeded) { context.Response.StatusCode = StatusCodes.Status401Unauthorized; return; } await next(context); } }修正TokenHandler潜在问题
- 避免强制转换
context.Resource,改用类型检查:if (context.Resource is HttpContext httpContext) { // 处理逻辑 } - 捕获
tokenService.DecryptToken可能抛出的异常,避免未捕获异常导致500错误:try { string jwtToken = httpContext.Request.Headers["Authorization"].ToString(); JwtSecurityToken token = tokenService.DecryptToken(jwtToken); // 角色验证逻辑 } catch (Exception) { context.Fail(); }
- 避免强制转换
内容的提问来源于stack exchange,提问作者azhar rahi
相关产品推荐
相关产品推荐

