我是.NET Core的新手,我试图在一个.NET Core 3.1项目中设置基于角色的授权。我相信我点击了每一个在线谈论它的教程和线程。我的问题是,在教程上似乎很容易实现,但对我来说却无法实现。根据我找到的教程,我所要做的就是给数据库中的用户分配一个角色,然后使用 [Authorize(Roles="roleName")]
前的Controller's Action。当我这样做时,我总是得到一个403错误,因为用户有指定的角色。当我使用 userManager.GetRolesAsync(user)
我看到该用户拥有该角色。当我用[Authorize]向这个动作发出请求时,当用户登录时,它就会像预期的那样工作。
我在调试模式下检查了当前用户的ClaimsPrincipal.Identity,结果发现 RoleClaimType = "role"
. 我检查了当前用户的权限,发现它没有一个类型为 "角色 "的权限。是这样的吗?[Authorize(Roles="...")]
工作?它看起来像一个索赔吗?如果是这样,我如何为用户的角色申请?在这个应用程序中,用户登录的唯一方法是用谷歌帐户。那么,如果他们是由谷歌登录管理,我应该如何添加一个索赔?
下面是我在Startup.cs中的代码。
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseNpgsql(Configuration.GetConnectionString("DefaultConnection")));
services.AddDefaultIdentity<ApplicationUser>()
.AddRoles<ApplicationRole>()
.AddEntityFrameworkStores<ApplicationDbContext>();
services.AddIdentityServer()
.AddApiAuthorization<ApplicationUser, ApplicationDbContext>();
services.AddAuthentication()
.AddGoogle(options =>
{
IConfigurationSection googleAuthNSection =
Configuration.GetSection("Authentication:Google");
options.ClientId = googleAuthNSection["ClientId"];
options.ClientSecret = googleAuthNSection["ClientSecret"];
})
.AddIdentityServerJwt();
services.AddControllersWithViews();
services.AddRazorPages();
services.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = "ClientApp/dist";
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
if (!env.IsDevelopment())
{
app.UseSpaStaticFiles();
}
app.UseRouting();
app.UseIdentityServer();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller}/{action=Index}/{id?}");
endpoints.MapRazorPages();
});
app.UseSpa(spa =>
{
spa.Options.SourcePath = "ClientApp";
if (env.IsDevelopment())
{
spa.UseAngularCliServer(npmScript: "start");
}
});
}
下面是一个Controller的Action例子
[Authorize(Roles = "Admin")]
[HttpGet("userinformations")]
public async Task<UserInformations> GetCurrentUserInformations()
{
string strUserId = this.User.FindFirstValue(ClaimTypes.NameIdentifier);
ApplicationUser user = await userManager.FindByIdAsync(strUserId);
string[] roles = (await userManager.GetRolesAsync(user)).ToArray();
UserInformations userInfo = new UserInformations()
{
UserName = user.UserName,
FirstName = user.FirstName,
LastName = user.LastName,
Email = user.Email,
Organization = user.idDefaultOrganisation.HasValue ? user.DefaultOrganization.OrganizationName : "",
Claims = this.User.Claims.Select(c => $"{c.Type} : {c.Value}").ToArray(),
Roles = roles
};
return userInfo;
}
当我在没有[Authorize(Roles = "Admin")]的情况下对这个Action进行请求时,我可以看到当前用户有Admin这个角色,但是当我添加这个角色时,却得到一个403错误。
我到底做错了什么?我觉得我好像少了一行或者类似的东西,因为在我找到的教程中,这一切看起来都很简单。
你的假设是正确的,当你指定了 [Authorize(Roles = "<role>")]
属性,ASP将创建一个 RolesAuthorizationRequirement
幕后。
然后授权处理程序将调用 this.HttpContext.User.IsInRole(<role>)
来评估策略。
在您的情况下,调用的是 this.HttpContext.User.IsInRole("Admin")
该方法 User.IsInRole
将调查一项名为 "http://schemas.microsoft.com/ws/2008/06/identity/claims/role"
并将其值与 "Admin "进行比较
ASP授权管道并没有连接到你的UserManager逻辑,基本的API只会观察和验证JWT令牌的要求。
你可能应该创建自己的AuthorizationHandler来检查用户是否真的是Admin。
或者使用RequireAssertion这种不太正式的方式。
services.AddAuthorization(options => options.AddPolicy("Admininstrators", builder =>
{
builder.RequireAssertion(async context =>
{
string strUserId = context.User.FindFirstValue(ClaimTypes.NameIdentifier);
var user = await userManager.FindByIdAsync(strUserId);
string[] roles = (await userManager.GetRolesAsync(user)).ToArray();
return roles.Contains("Admin");
};
});
[Authorize("Admininstrators")]
[HttpGet("userinformations")]
public async Task<UserInformations> GetCurrentUserInformations()
{
...
}
我终于找到了一个可行的解决方案。我试着用RequireAssertion来改编@MichaelShterenberg的代码,但我不能让它工作,因为我必须查询我的数据库,而且我不能用这个解决方案来使用UserManager。最后,我根据他的答案中的这部分找到了一个解决方案。
你可能应该创建自己的AuthorizationHandler,检查用户是否真的是Admin。
我按照这个帖子的答案。Dependency Injection on AuthorizationOptions Requirement in DotNet Core