为什么 ASP.NET Core Identity 2.0 Authorize 过滤器导致我得到 404?
Why is ASP.NET Core Identity 2.0 Authorize filter causing me to get a 404?
我有一个控制器,我只想将其限制为特定角色,比方说 admin
。在为用户设置 admin
角色后,我可以使用 IsInRoleAsync
方法(returns 为真)验证他是否在该角色上。当使用 [Authorize(Roles = "admin")]
设置属性时,我得到一个 404 with that very same user 。我正在使用不记名令牌(我认为这无关紧要,但无论如何),这是我尝试调试所做的工作:
Controller w/o [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>();
我认为您需要检查声明,而不是角色。添加一个 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;
}));
});
您可能会收到 404 响应,因为身份 - services.AddIdentity()
自动配置为默认身份验证的 sign-in/sign-out 和 challenge/forbidden 方案 - 试图将您重定向到 "access denied page"(Account/AccessDenied
默认情况下),您的应用程序中可能不存在。
尝试覆盖默认的 challenge/forbidden 方案,看看它是否能解决您的问题:
services.AddAuthentication(options =>
{
// ...
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultForbidScheme = JwtBearerDefaults.AuthenticationScheme;
});
要解决第二个问题,请确保禁用 JWT 声明映射功能。如果不是,JWT 处理程序将 "convert" 您对 ClaimTypes.Role
的所有 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>()
});
});
我有一个控制器,我只想将其限制为特定角色,比方说 admin
。在为用户设置 admin
角色后,我可以使用 IsInRoleAsync
方法(returns 为真)验证他是否在该角色上。当使用 [Authorize(Roles = "admin")]
设置属性时,我得到一个 404 with that very same user 。我正在使用不记名令牌(我认为这无关紧要,但无论如何),这是我尝试调试所做的工作:
Controller w/o [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>();
我认为您需要检查声明,而不是角色。添加一个 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;
}));
});
您可能会收到 404 响应,因为身份 - services.AddIdentity()
自动配置为默认身份验证的 sign-in/sign-out 和 challenge/forbidden 方案 - 试图将您重定向到 "access denied page"(Account/AccessDenied
默认情况下),您的应用程序中可能不存在。
尝试覆盖默认的 challenge/forbidden 方案,看看它是否能解决您的问题:
services.AddAuthentication(options =>
{
// ...
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultForbidScheme = JwtBearerDefaults.AuthenticationScheme;
});
要解决第二个问题,请确保禁用 JWT 声明映射功能。如果不是,JWT 处理程序将 "convert" 您对 ClaimTypes.Role
的所有 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>()
});
});