.NET Core 3.1 JWTBearer认证返回401 Unauthorized问题排查
.NET Core 3.1 JWT认证返回401问题修复
核心问题点
你的代码存在3处直接导致认证失败的问题:
- 路由模式混用、中间件顺序错误
你在服务配置中设置EnableEndpointRouting = false关闭了端点路由,要走传统MVC路由逻辑,但管道里同时注册了UseMvc()和UseRouting()/UseEndpoints()两套路由组件,且UseMvc()放在了UseAuthentication()、UseAuthorization()前面。请求进入管道后会先被UseMvc()匹配并进入MVC授权过滤器流程,此时认证中间件还未执行,HttpContext.User没有被JWT处理器填充身份信息,[Authorize]特性直接判定未授权返回401,后续的认证、授权中间件根本不会处理该请求。 - 服务注册语法错误
ConfigureServices中services.AddControllers();末尾多写了分号,导致后续.AddMvc()的链式调用完全不生效,MVC配置(包括关闭端点路由的设置)没有正确注入容器,进一步导致路由执行逻辑混乱。 - Token生成时间隐患
生成JWT时使用DateTime.Now取本地时间,如果服务器时区和JWT校验逻辑的时区存在偏差,可能出现token刚生成就被判定过期的问题,建议统一使用UTC时间。
修复方案
两种路由模式二选一即可,官方推荐使用端点路由模式,禁止两套路由逻辑混用。
方案1:使用端点路由(推荐)
- 修正服务注册代码,移除多余分号,删除关闭端点路由的配置:
public void ConfigureServices(IServiceCollection services) { services .AddAuthentication(options => { options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }) .AddJwtBearer(options => { var key = Encoding.UTF8.GetBytes("thisismycustomSecretkeyforauthentication"); options.SaveToken = true; options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = false, ValidateAudience = false, ValidateLifetime = true, ClockSkew = TimeSpan.Zero, ValidateIssuerSigningKey = true, IssuerSigningKey = new SymmetricSecurityKey(key) }; }); services.AddControllers(); }
- 修正中间件管道顺序,移除
UseMvc(),严格按照端点路由要求的顺序注册:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); app.UseSpa(spa => spa.UseProxyToSpaDevelopmentServer("https://localhost:22010")); } else { app.UseHsts(); } app.UseStaticFiles(); app.UseHttpsRedirection(); // 端点路由固定顺序:路由匹配 -> 认证 -> 授权 -> 端点执行 app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); }
- 修正JWT生成的时间逻辑:
var token = new JwtSecurityToken ( issuer: null, audience: null, claims: new Claim[] { new Claim(ClaimTypes.Name, request.Username) }, expires: DateTime.UtcNow.AddDays(30), signingCredentials: new SigningCredentials(tokenKey, SecurityAlgorithms.HmacSha256) );
方案2:使用传统MVC路由
如果必须使用传统路由,完全移除UseRouting()、UseEndpoints()相关代码,将认证、授权中间件放在UseMvc()之前注册:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { // 其他通用中间件(HSTS、HTTPS跳转、静态文件等)保持不变 app.UseHttpsRedirection(); // 认证授权必须在MVC路由之前执行 app.UseAuthentication(); app.UseAuthorization(); app.UseMvc(routes => routes.MapRoute(name: "default", template: "{controller=App}/{action=Index}/{id?}")); if (env.IsDevelopment()) app.UseSpa(spa => spa.UseProxyToSpaDevelopmentServer("https://localhost:22010")); }
同时修正ConfigureServices中AddControllers()后的多余分号。
调试手段
如果修复后仍存在问题,可以通过JWT事件监听定位具体失败原因:
.AddJwtBearer(options => { // 其余配置保持不变 options.Events = new JwtBearerEvents { OnAuthenticationFailed = context => { // 在此处下断点,查看context.Exception的具体信息,可明确是签名不匹配、token过期、格式错误等具体原因 return Task.CompletedTask; } }; });
另外请求受保护接口时,确认请求头中Authorization字段格式正确,为Bearer <你的JWT字符串>,Bearer和token之间需要保留一个空格。
内容的提问来源于stack exchange,提问作者Santi
相关产品推荐
相关产品推荐

