C# ASP.NET Core 3.1中基于角色的授权,带有标识和外部登录

C# ASP.NET Core 3.1中基于角色的授权,带有标识和外部登录,c#,asp.net,identityserver4,asp.net-core-3.1,role-based-access-control,C#,Asp.net,Identityserver4,Asp.net Core 3.1,Role Based Access Control,我是.NETCore的新手,我正在尝试在.NETCore3.1项目中设置基于角色的授权。我相信我点击了每一篇教程和在线讨论的帖子。我的问题是,它似乎很容易在教程上工作,但它不适合我。根据我找到的教程,我所要做的就是在数据库中为用户分配一个角色,然后在控制器执行操作之前使用[Authorize(Roles=“roleName”)]。当我这样做时,对于具有指定角色的用户,我总是会收到403错误。当我使用userManager.GetRolesAsync(user)时,我看到用户拥有这个角色。当我使用

我是.NETCore的新手,我正在尝试在.NETCore3.1项目中设置基于角色的授权。我相信我点击了每一篇教程和在线讨论的帖子。我的问题是,它似乎很容易在教程上工作,但它不适合我。根据我找到的教程,我所要做的就是在数据库中为用户分配一个角色,然后在控制器执行操作之前使用
[Authorize(Roles=“roleName”)]
。当我这样做时,对于具有指定角色的用户,我总是会收到403错误。当我使用
userManager.GetRolesAsync(user)
时,我看到用户拥有这个角色。当我使用[Authorize]请求此操作时,它会在用户登录时正常工作

我检查了当前用户的调试模式ClaimsPrincipal.Identity,发现
RoleClaimType=“role”
。我检查了当前用户的声明,发现它没有类型为“role”的声明。
[Authorize(Roles=“…”)
就是这样工作的吗?它看起来像是一个有争议的声明吗?如果是,我如何声明用户的角色?用户登录此应用程序的唯一方法是使用Google帐户。那么,如果它们是由谷歌登录管理的,我该如何添加声明呢

这是我在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");
            }
    });
}
public void配置服务(IServiceCollection服务)
{
services.AddDbContext(选项=>
options.UseNpgsql(Configuration.GetConnectionString(“DefaultConnection”));
services.AddDefaultIdentity()
.AddRoles()
.AddEntityFrameworkStores();
services.AddIdentityServer()
.addapi授权();
services.AddAuthentication()
.AddGoogle(选项=>
{
IConfigurationSection googleAuthNSection=
GetSection(“身份验证:Google”);
options.ClientId=googleAuthNSection[“ClientId”];
options.ClientSecret=googleAuthNSection[“ClientSecret”];
})
.AddIdentityServerJwt();
services.AddControllersWithViews();
services.AddRazorPages();
services.AddSpaStaticFiles(配置=>
{
configuration.RootPath=“ClientApp/dist”;
});
}
public void配置(IApplicationBuilder应用程序、IWebHostEnvironment环境)
{
if(env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
}
其他的
{
app.UseExceptionHandler(“/Error”);
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
如果(!env.IsDevelopment())
{
app.UseSpaStaticFiles();
}
app.UseRouting();
app.UseIdentityServer();
app.UseAuthorization();
app.UseEndpoints(端点=>
{
endpoints.MapControllerRoute(
名称:“默认”,
模式:“{controller}/{action=Index}/{id?}”);
endpoints.MapRazorPages();
});
app.UseSpa(spa=>
{
spa.Options.SourcePath=“ClientApp”;
if(env.IsDevelopment())
{
spa.UseAngularCliServer(npmScript:“启动”);
}
});
}
下面是一个控制器动作的示例

[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”)]
[HttpGet(“用户信息”)]
公共异步任务GetCurrentUserInformations()
{
string strUserId=this.User.FindFirstValue(ClaimTypes.NameIdentifier);
ApplicationUser user=await userManager.FindByIdAsync(strUserId);
字符串[]角色=(等待userManager.GetRolesAsync(用户)).ToArray();
UserInformations userInfo=新的UserInformations()
{
UserName=user.UserName,
FirstName=user.FirstName,
LastName=user.LastName,
Email=user.Email,
组织=user.idDefaultOrganization.HasValue?user.DefaultOrganization.OrganizationName:“”,
Claims=this.User.Claims.Select(c=>$“{c.Type}:{c.Value}”).ToArray(),
角色=角色
};
返回用户信息;
}
当我在没有[Authorize(Roles=“Admin”)]的情况下对此操作发出请求时,我可以看到当前用户具有Admin角色,但当我添加该角色时,会出现403错误


我做错了什么?我觉得我在某个地方遗漏了一行或类似的内容,因为在我找到的教程中,这一切似乎都很简单。

您的假设是正确的,当您指定
[Authorize(Roles=“”)]
属性时,ASP将在幕后创建
角色授权要求

然后授权处理程序将调用
this.HttpContext.User.IsInRole()
来评估策略

在您的情况下,调用是this.HttpContext.User.IsInRole(“Admin”)

方法
User.IsInRole
将查找名为
的声明http://schemas.microsoft.com/ws/2008/06/identity/claims/role
并将其值与“Admin”进行比较

ASP授权管道没有连接到UserManager逻辑,基本API将只观察和验证JWT令牌声明

您可能应该创建自己的AuthorizationHandler来检查用户是否确实是管理员

或者使用Requiremeartion的不太正式的方式:

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");
    };
});

[授权(“管理员”)]
[HttpGet(“用户信息”)]
公共异步任务GetCurrentUserInformations()
{
...
}

我终于找到了一个可行的解决方案。 我尝试使用RequireAssertion来修改@MichaelShterenberg的代码,但是我无法让它工作,因为我必须查询我的数据库,并且我无法使用UserManager来解决这个问题。 我最终根据他的回答找到了一个解决方案:

您可能应该创建自己的AuthorizationHandler来检查用户是否确实是管理员


我遵循了这条线索的答案:

首先,感谢您的回答。现在我知道它使用了这个声明,我如何根据数据库的内容来创建声明呢?有几种方法可以做到这一点:最标准的
[Authorize("Admininstrators")]
[HttpGet("userinformations")]
public async Task<UserInformations> GetCurrentUserInformations()
{
   ...
}