You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

调用需is_admin Claim的端点时出现404 NOT FOUND错误求助

问题排查:/admin/page端点返回404但用户已持有is_admin Claim

现有配置代码

服务配置

var services = builder.Services;
services.AddAuthentication(IdentityConstants.BearerScheme)
  .AddBearerToken(IdentityConstants.BearerScheme);
services.AddAuthorizationBuilder()
  .SetDefaultPolicy(new([new DenyAnonymousAuthorizationRequirement()], 
                    [IdentityConstants.BearerScheme]));
services.AddIdentity<AppUser, IdentityRole>()
  .AddEntityFrameworkStores<AppDbContext>()
  .AddClaimsPrincipalFactory<AppUserClaimsPrincipalFactory>();

AppUser类

public class AppUser : IdentityUser
{
  public string? Locale { get; set; }
  public bool? IsAdmin { get; set; }
}

AppUserClaimsPrincipalFactory类

public class AppUserClaimsPrincipalFactory
  : UserClaimsPrincipalFactory<AppUser>
{
  public AppUserClaimsPrincipalFactory(
    UserManager<AppUser> userManager,
    IOptions<IdentityOptions> optionsAccessor)
    : base(userManager, optionsAccessor) { }
  protected override async Task<ClaimsIdentity> GenerateClaimsAsync(AppUser user)
  {
    var identity = await base.GenerateClaimsAsync(user);
    if (user.Locale is not null)
    {
      identity.AddClaim(new Claim("locale", user.Locale));
    }
    if (user.IsAdmin == true) //IsAdmin is bool?
    {
      identity.AddClaim(new Claim("is_admin", "is_admin"));
    }
    return identity;
  }
}

出错的端点

app.MapGet("/admin/page", () => Results.Ok("admin page"))
  .RequireAuthorization(config => config.RequireClaim("is_admin"));

验证Claim的端点

app.MapGet("/claims", async (SignInManager<AppUser> signInManager, HttpContext http) =>
{
  var user = await signInManager.UserManager.GetUserAsync(http.User);
  if (user is null)
  {
    return Results.BadRequest("User not found");
  }
  var principal = await signInManager.CreateUserPrincipalAsync(user);
  return Results.Ok(principal.Claims);
})
  .RequireAuthorization();

问题描述

调用/admin/page端点时返回404 Not Found错误,但通过/claims端点已确认用户持有所需的is_admin Claim,需要排查原因。

可能的原因及解决方法

1. Bearer Token中未包含is_admin Claim

SignInManager.CreateUserPrincipalAsync是直接从数据库用户实体生成Principal,但Bearer Token的Claims是在颁发Token时确定的。默认AddBearerToken不会自动将自定义Claim(如is_admin、locale)添加到JWT中,导致认证后HttpContext.User中没有该Claim,最终授权失败,部分场景下会被返回为404而非预期的403。

解决方法:配置AddBearerToken的事件,在生成Token时添加自定义Claim:

services.AddAuthentication(IdentityConstants.BearerScheme)
  .AddBearerToken(IdentityConstants.BearerScheme, options =>
  {
      options.Events = new BearerTokenEvents
      {
          async function OnCreatingToken(context)
          {
              var userManager = context.HttpContext.RequestServices.GetRequiredService<UserManager<AppUser>>();
              var user = await userManager.GetUserAsync(context.Principal);
              
              if (user?.IsAdmin == true)
              {
                  context.Claims.Add(new Claim("is_admin", "is_admin"));
              }
              if (!string.IsNullOrEmpty(user?.Locale))
              {
                  context.Claims.Add(new Claim("locale", user.Locale));
              }
          }
      };
  });

2. 授权策略未指定认证方案

默认策略指定了IdentityConstants.BearerScheme,但RequireAuthorization(config => config.RequireClaim("is_admin"))创建的是全新策略,未继承默认策略的认证方案配置。这会导致认证过程未使用Bearer Token方案,无法正确验证用户身份,进而引发路由匹配异常或错误返回404。

解决方法:在授权配置中显式指定认证方案:

app.MapGet("/admin/page", () => Results.Ok("admin page"))
  .RequireAuthorization(config => 
  {
      config.RequireClaim("is_admin");
      config.AddAuthenticationSchemes(IdentityConstants.BearerScheme);
  });

或者提前定义包含认证方案的管理员策略:

services.AddAuthorizationBuilder()
  .SetDefaultPolicy(new AuthorizationPolicyBuilder(IdentityConstants.BearerScheme)
      .RequireAuthenticatedUser()
      .Build())
  .AddPolicy("AdminPolicy", policy => 
      policy.RequireClaim("is_admin")
            .AddAuthenticationSchemes(IdentityConstants.BearerScheme));

然后端点使用该策略:

app.MapGet("/admin/page", () => Results.Ok("admin page"))
  .RequireAuthorization("AdminPolicy");

3. 端点注册顺序问题

如果/admin/page端点注册在路由中间件(app.UseRouting())之前,或者被更通用的路由(如MapGet("{*path}", ...))覆盖,也会导致404。确保端点注册在app.UseAuthorization()之后,且没有优先级更高的路由拦截请求。

内容的提问来源于stack exchange,提问作者JohnyL

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 05:17:12