【问题标题】:Why is ASP.NET Core Identity 2.0 Authorize filter causing me to get a 404?为什么 ASP.NET Core Identity 2.0 授权过滤器导致我得到 404?
【发布时间】:2023-03-20 08:16:01
【问题描述】:

我有一个控制器,我只想限制为特定角色,比如admin。在为用户设置admin 角色后,我可以使用IsInRoleAsync 方法(返回true)验证他是否在该角色上。使用 [Authorize(Roles = "admin")] 设置属性时,我得到一个 404 与同一个用户。我正在使用不记名令牌(我认为这无关紧要,但无论如何),这是我为尝试调试所做的:

不带[Authorize] 的控制器:返回资源。 [好的]

带有[Authorize] 的控制器:仅在我使用Authentication: Bearer [access token] 时返回资源 [OK]

带有[Authorize(Roles = "admin")] 的控制器:即使在使用设置了角色的用户登录后,我也会收到 404 [NOK]

我不知道我是否缺少某些配置,但这是我的 ConfigureServices:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();

    // Add framework services.
    services.AddDbContext<ApplicationDbContext>(options =>
    {
        options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"));
        options.UseOpenIddict();
    });
    services.AddIdentity<ApplicationUser, IdentityRole>()
        .AddEntityFrameworkStores<ApplicationDbContext>()
        .AddDefaultTokenProviders();

    services.AddOpenIddict(opt =>
    {
        opt.AddEntityFrameworkCoreStores<ApplicationDbContext>();
        opt.AddMvcBinders();
        opt.EnableTokenEndpoint("/api/token");
        opt.AllowPasswordFlow();
        opt.DisableHttpsRequirement(); //for dev only!
        opt.UseJsonWebTokens();
        opt.AddEphemeralSigningKey();
        opt.AllowRefreshTokenFlow();
        opt.SetAccessTokenLifetime(TimeSpan.FromMinutes(5));
    });

    services.AddAuthentication(options =>
    {
        options.DefaultScheme = OAuthValidationDefaults.AuthenticationScheme;
        options.DefaultAuthenticateScheme = OAuthValidationConstants.Schemes.Bearer;
        options.DefaultSignInScheme = IdentityConstants.ExternalScheme;
    })
       .AddJwtBearer(options =>
       {
           options.Authority = "http://localhost:44337/";
           options.Audience = "resource_server";
           options.RequireHttpsMetadata = false;
           options.TokenValidationParameters = new TokenValidationParameters
           {
               NameClaimType = OpenIdConnectConstants.Claims.Subject,
               RoleClaimType = OpenIdConnectConstants.Claims.Role
           };                   
       });
    services.Configure<IdentityOptions>(options =>
    {
        // Password settings
        options.Password.RequireDigit = true;
        options.Password.RequiredLength = 8;
        options.Password.RequireNonAlphanumeric = false;
        options.Password.RequireUppercase = true;
        options.Password.RequireLowercase = false;

        // Lockout settings
        options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(30);
        options.Lockout.MaxFailedAccessAttempts = 10;
        // User settings
        options.User.RequireUniqueEmail = true;
        // Add application services.
        options.ClaimsIdentity.UserNameClaimType= OpenIdConnectConstants.Claims.Name;
        options.ClaimsIdentity.UserIdClaimType = OpenIdConnectConstants.Claims.Subject;
        options.ClaimsIdentity.RoleClaimType = OpenIdConnectConstants.Claims.Role;
    });

    services.AddSingleton(typeof(RoleManager<ApplicationUser>));
    // Add application services.
    services.AddTransient<IEmailSender, AuthMessageSender>();
    services.AddTransient<ISmsSender, AuthMessageSender>();

【问题讨论】:

    标签: c# asp.net-core asp.net-core-2.0 asp.net-core-identity


    【解决方案1】:

    您可能会收到 404 响应,因为 Identity - 由 services.AddIdentity() 自动配置为默认身份验证、登录/注销和质询/禁止方案 - 试图将您重定向到“拒绝访问页面”( Account/AccessDenied 默认情况下),这可能在您的应用程序中不存在。

    尝试覆盖默认的质询/禁止方案,看看它是否能解决您的问题:

    services.AddAuthentication(options =>
    {
        // ...
        options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
        options.DefaultForbidScheme = JwtBearerDefaults.AuthenticationScheme;
    });
    

    要解决您的第二个问题,请确保禁用 JWT 声明映射功能。如果不是,JWT 处理程序会将您的所有 role 声明“转换”为 ClaimTypes.Role,这将不起作用,因为您将其配置为使用 role 作为 ClaimsPrincipal.IsInRole(...) (RoleClaimType = OpenIdConnectConstants.Claims.Role) 使用的角色声明.

    services.AddAuthentication(options =>
    {
        options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
    })
    .AddJwtBearer(options =>
    {
        // ...
        options.SecurityTokenValidators.Clear();
        options.SecurityTokenValidators.Add(new JwtSecurityTokenHandler
        {
            // Disable the built-in JWT claims mapping feature.
            InboundClaimTypeMap = new Dictionary<string, string>()
        });
    });
    

    【讨论】:

    • 好吧,这解释并解决了 404,但是,为什么我无法访问内容,因为我的用户具有所需的角色?现在我得到一个 403
    • @DanielS。答案已更新,并修复了您的第二个问题。
    • 正如您所说,我的问题是我的令牌上没有“角色”,因为我认为如果用户分配了该角色,身份将检查 AspNetUserRoles。谢谢你的回答
    【解决方案2】:

    我认为您需要的是检查声明,而不是角色。添加AuthorizeAttribute 如:

    [Authorize(Policy = "AdminOnly")]
    

    然后配置一个需要声明的策略:

    services.AddAuthorization(options =>
    {
        options.AddPolicy("AdminOnly", policy =>
                          policy.RequireClaim(OpenIdConnectConstants.Claims.Role, "Admin"));
    });
    

    或者,出于调试目的或更高级的验证,您可以:

    services.AddAuthorization(options =>
    {
        options.AddPolicy("AdminOnly", policy =>
                          policy.RequireAssertion(ctx =>
       {
           //do your checks
           return true;
       }));
    });
    

    【讨论】:

    • 我会试试的,虽然在microsoft docs 上似乎不需要。我的意思是,听起来像是不同的东西。
    • 角色有点老派,现在都是关于声明的。
    • 即使使用该策略,它也会返回 404
    • 要调试,您可以添加 RequireAssertion 而不是 RequireClaim。从那里你得到请求上下文,你可以返回 true 或 false。
    • 这个运气好吗?
    猜你喜欢
    • 1970-01-01
    • 2018-05-12
    • 2018-04-03
    • 2022-09-27
    • 2017-02-10
    • 1970-01-01
    • 2020-08-20
    • 1970-01-01
    • 2016-10-27
    相关资源
    最近更新 更多