简体   繁体   English

为什么ASP.NET Core Identity 2.0授权过滤器导致我获得404?

[英]Why is ASP.NET Core Identity 2.0 Authorize filter causing me to get a 404?

I have a controller that I want to restrict only to a specific role, let's say admin . 我有一个控制器,我想只限制一个特定的角色,让我们说admin After setting a user with the admin role, I can validate that he's on that role using the IsInRoleAsync method (which returns true). 在使用admin角色设置用户之后,我可以使用IsInRoleAsync方法(返回true)验证他是否在该角色上。 When setting the attribute with [Authorize(Roles = "admin")] I get a 404 with that very same user . 使用[Authorize(Roles = "admin")]设置属性时,我得到的是同一个用户的404。 I'm using bearer tokens (I don't think that is relevant but anyway) and here's what I've done to try debugging: 我正在使用持有者令牌(我不认为这是相关的,但无论如何),这是我尝试调试所做的:

Controller w/o [Authorize] : the resource is returned. 控制器没有[Authorize] :返回资源。 [OK] [好]

Controller with [Authorize] : the resource is returned only when I use the Authentication: Bearer [access token] [OK] [Authorize]控制器: 当我使用Authentication: Bearer [access token] 时才返回资源Authentication: Bearer [access token] [确定]

Controller with [Authorize(Roles = "admin")] : even after logging in with the user that has the role set, I get the 404 [NOK] [Authorize(Roles = "admin")]控制器:即使在拥有角色集的用户登录后,我也得到404 [NOK]

I don't know if I'm missing some configuration, but here's my ConfigureServices: 我不知道我是否缺少一些配置,但这是我的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>();

You likely get a 404 response because Identity - which is automatically configured as the default authentication, sign-in/sign-out and challenge/forbidden scheme by services.AddIdentity() - tries to redirect you to the "access denied page" ( Account/AccessDenied by default), that probably doesn't exist in your application. 您可能会收到404响应,因为身份 - 自动配置为服务的默认身份验证,登录/注销和质询/禁止方案services.AddIdentity() - 尝试将您重定向到“访问被拒绝的页面”( Account/AccessDenied默认情况下为Account/AccessDenied ),这可能在您的应用程序中不存在。

Try to override the default challenge/forbidden scheme to see if it fixes your issue: 尝试覆盖默认的挑战/禁止方案,看看它是否修复了您的问题:

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

To fix your second issue, make sure the JWT claims mapping feature is disabled. 要解决第二个问题,请确保已禁用JWT声明映射功能。 If it's not, the JWT handler will "convert" all your role claims to ClaimTypes.Role , which won't work as you configured it to use role as the role claim used by ClaimsPrincipal.IsInRole(...) ( RoleClaimType = OpenIdConnectConstants.Claims.Role ). 如果不是,JWT处理程序会将您的所有role声明“转换”为ClaimTypes.Role ,当您将其配置为使用role作为ClaimsPrincipal.IsInRole(...)使用的角色声明时, 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>()
    });
});

I think that what you need is to check claims, not roles. 我认为你需要的是检查索赔,而不是角色。 Add an AuthorizeAttribute such as: 添加AuthorizeAttribute例如:

[Authorize(Policy = "AdminOnly")]

And then configure a policy that requires a claim: 然后配置需要声明的策略:

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

Or, for debugging purposes or more advanced validation, you could have: 或者,出于调试目的或更高级的验证,您可以:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM