[英]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.