IdentityServer4自定义AuthenticationHandler找不到用户的所有声明 [英] IdentityServer4 custom AuthenticationHandler can't find all claims for a user

查看:107
本文介绍了IdentityServer4自定义AuthenticationHandler找不到用户的所有声明的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用使用Asp.Net身份和EntityFramework的IdentityServer4示例.

I am using the IdentityServer4 sample that uses Asp.Net Identity and EntityFramework.

我正在尝试使用基于声明/角色的自定义策略来创建组控件.

I am trying to create group controls using custom policies based on claims/roles.

我的问题是,当我尝试在授权处理程序中获取用户声明时,我要查找的声明不会返回.

My problem is that when I try and get the users claims in the authorization handler the claims I am looking for are not returned.

查看SSMS中的数据库,我发现我创建的声明/角色位于名为"AspNetRoles","AspNetRoleClaims","AspNetUserClaims"的表中,以及我在"AspNetUsers"中创建的用户以及用户和角色的键在"AspNetUserRoles"中. 当我打电话给用户寻求授权的索赔时,索赔清单似乎来自"IdentityClaims"表.

Looking at the database in SSMS I find the claims/roles I created are in tables called "AspNetRoles", "AspNetRoleClaims", "AspNetUserClaims" along with the user I created being in "AspNetUsers" and keys for the user and role being in "AspNetUserRoles". When I call to get the users claims for authorization the list of claims seems to come from the "IdentityClaims" table.

似乎没有一种简单的方法可以检查"AspNetClaims"中的声明,就像"IdentityClaims"中的声明一样,所以我认为我在某个地方出错了.

There doesn't seem to be a simple way to check for claims in "AspNetClaims" like there is for claims in "IdentityClaims" so I assume I've made an error somewhere.

我一直在寻找解决方案,并尝试了一些尝试,但找不到任何可行的方法.

I've looked around a fair bit for a solution and tried a fair few things but I can't find anything that works.

下面是我认为与该问题最相关的代码,以及一些正在运行的代码的屏幕截图.

Below is the code i thought would me most relevant to the question along with some screenshots taken of the running code.

任何帮助将不胜感激,在此先感谢.

Any help would be much appreciated, thanks in advance.

MvcClient.Startup

MvcClient.Startup

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();

    JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();

    services.AddAuthorization(options =>
    {
        options.AddPolicy("AdminRights", policy =>
        {                 
            policy.Requirements.Add(new AdminRequirement());
            policy.RequireAuthenticatedUser();
            policy.AddAuthenticationSchemes("Cookies");
        });
    });

    services.AddSingleton<IAuthorizationHandler, AdminRequirementHandler>();

    services.AddAuthentication(options =>
        {
            options.DefaultScheme = "Cookies";
            options.DefaultChallengeScheme = "oidc";
        })
        .AddCookie("Cookies")
        .AddOpenIdConnect("oidc", options =>
        {
            options.SignInScheme = "Cookies";

            options.Authority = "http://localhost:5000";
            options.RequireHttpsMetadata = false;

            options.ClientId = "mvc";
            options.ClientSecret = "secret";
            options.ResponseType = "code id_token token"; // NEW CHANGE (token)

            options.SaveTokens = true;
            options.GetClaimsFromUserInfoEndpoint = true;

            options.Scope.Add("api1");
            options.Scope.Add("AdminPermission"); // NEW CHANGE
            options.Scope.Add("offline_access");
        });
}

MvcClient.Controllers.HomeController

MvcClient.Controllers.HomeController

[Authorize(Policy = "AdminRights")]
public IActionResult Administrator()
{
    return View();
}

IdentityServerWithAspIdAndEF.Startup(在配置"中最后一次调用)

IdentityServerWithAspIdAndEF.Startup (Called last in Configure)

private async Task CreateSuperuser(IServiceProvider serviceProvider, ApplicationDbContext context)
{
    //adding custom roles
    var RoleManager = serviceProvider.GetRequiredService<RoleManager<IdentityRole>>();
    var UserManager = serviceProvider.GetRequiredService<UserManager<ApplicationUser>>();

    string[] roleNames = { "Administrator", "Internal", "Customer" };

    foreach (var roleName in roleNames)
    {
        //creating the roles and seeding them to the database
        var roleExist = await RoleManager.RoleExistsAsync(roleName);

        if (roleExist)
            await RoleManager.DeleteAsync( await RoleManager.FindByNameAsync(roleName) );

        var newRole = new IdentityRole(roleName);
        await RoleManager.CreateAsync(newRole);

        if(roleName == "Administrator")
            await RoleManager.AddClaimAsync(newRole, new Claim("AdminPermission", "Read"));
    }

    //creating a super user who could maintain the web app
    var poweruser = new ApplicationUser
    {
        UserName = Configuration.GetSection("UserSettings")["UserEmail"],
        Email = Configuration.GetSection("UserSettings")["UserEmail"]
    };

    string UserPassword = Configuration.GetSection("UserSettings")["UserPassword"];


    var _user = await UserManager.FindByEmailAsync(Configuration.GetSection("UserSettings")["UserEmail"]);

    if (_user != null)
        await UserManager.DeleteAsync( await UserManager.FindByEmailAsync(Configuration.GetSection("UserSettings")["UserEmail"]) );

    var createPowerUser = await UserManager.CreateAsync(poweruser, UserPassword);

    if (createPowerUser.Succeeded)
    {
        //here we tie the new user to the "Admin" role 
        await UserManager.AddToRoleAsync(poweruser, "Administrator");
        await UserManager.AddClaimAsync(poweruser, new Claim("AdminPermission", "Create"));
        await UserManager.AddClaimAsync(poweruser, new Claim("AdminPermission", "Update"));
        await UserManager.AddClaimAsync(poweruser, new Claim("AdminPermission", "Delete"));

    }
}

IdentityServerWithAspIdAndEF.Config

IdentityServerWithAspIdAndEF.Config

public class Config
{
    // scopes define the resources in your system
    public static IEnumerable<IdentityResource> GetIdentityResources()
    {
        return new List<IdentityResource>
        {
            new IdentityResources.OpenId(),
            new IdentityResources.Profile(),
            new IdentityResource()  // NEW CHANGE
            {
                Name = "AdminPermission",
                DisplayName = "Admin Permission",
                UserClaims =
                {
                    "AdminPermission",
                }
            }
        };
    }

    public static IEnumerable<ApiResource> GetApiResources()
    {
        return new List<ApiResource>
        {
            new ApiResource("api1", "My API")
            {
                Scopes = // NEW CHANGE
                {
                    new Scope("AdminPermission", "Admin Permission")
                    {
                        UserClaims = { "AdminPermission" }
                    }
                }
            }
        };
    }

    // clients want to access resources (aka scopes)
    public static IEnumerable<Client> GetClients()
    {
        // client credentials client
        return new List<Client>
        {
            // OpenID Connect hybrid flow and client credentials client (MVC)
            new Client
            {
                ClientId = "mvc",
                ClientName = "MVC Client",
                AllowedGrantTypes = GrantTypes.HybridAndClientCredentials,

                RequireConsent = false, // NEW CHANGE (false)

                ClientSecrets = 
                {
                    new Secret("secret".Sha256())
                },

                RedirectUris = { "http://localhost:5002/signin-oidc" },
                PostLogoutRedirectUris = { "http://localhost:5002/signout-callback-oidc" },

                AllowedScopes =
                {
                    IdentityServerConstants.StandardScopes.OpenId,
                    IdentityServerConstants.StandardScopes.Profile,
                    "api1"
                    "AdminPermission", // NEW CHANGE
                },
                AllowOfflineAccess = true,
            },

            // Other Clients omitted as not used.
        };
    }
}

IdentityServerWithAspIdAndEF.Startup

IdentityServerWithAspIdAndEF.Startup

public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<ApplicationDbContext>(options =>
            options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

    services.AddIdentity<ApplicationUser, IdentityRole>()
        .AddEntityFrameworkStores<ApplicationDbContext>()
        .AddDefaultTokenProviders();

    // Add application services.
    services.AddTransient<IEmailSender, EmailSender>();

    services.AddMvc();

    string connectionString = Configuration.GetConnectionString("DefaultConnection");
    var migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name;

    // configure identity server with in-memory stores, keys, clients and scopes
    services.AddIdentityServer()
        .AddDeveloperSigningCredential()
        .AddAspNetIdentity<ApplicationUser>()
        // this adds the config data from DB (clients, resources)
        .AddConfigurationStore(options =>
        {
            options.ConfigureDbContext = builder =>
                    builder.UseSqlServer(connectionString,
                        sql => sql.MigrationsAssembly(migrationsAssembly));
        })
        // this adds the operational data from DB (codes, tokens, consents)
        .AddOperationalStore(options =>
        {
            options.ConfigureDbContext = builder =>
                    builder.UseSqlServer(connectionString,
                        sql => sql.MigrationsAssembly(migrationsAssembly));

            // this enables automatic token cleanup. this is optional.
            options.EnableTokenCleanup = true;
            options.TokenCleanupInterval = 30;
        });

    services.AddAuthentication()
        .AddGoogle("Google", options =>
        {
            options.ClientId = "434483408261-55tc8n0cs4ff1fe21ea8df2o443v2iuc.apps.googleusercontent.com";
                options.ClientSecret = "3gcoTrEDPPJ0ukn_aYYT6PWo";
        })
        .AddOpenIdConnect("oidc", "OpenID Connect", options =>
        {
            options.Authority = "https://demo.identityserver.io/";
            options.ClientId = "implicit";
            options.SaveTokens = true;

         // options.GetClaimsFromUserInfoEndpoint = true; // NEW CHANGE
         // options.ResponseType = "code id_token token";  // NEW CHANGE

            options.TokenValidationParameters = new TokenValidationParameters
            {
                NameClaimType = "name",
                RoleClaimType = "role"
            };
        });
}

MvcClient.Authorization

MvcClient.Authorization

public class AdminRequirementHandler : AuthorizationHandler<AdminRequirement>
{

    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, AdminRequirement requirement)
    {

        Console.WriteLine("User Identity: {0}", context.User.Identity);
        Console.WriteLine("Role is 'Administrator'? : {0}", context.User.IsInRole("Administrator"));
        Console.WriteLine("Identities of user:-");
        foreach (var v in context.User.Identities)
        {
            Console.WriteLine("\tName: {0},\tActor: {1},\tAuthType: {2},\tIsAuth: {3}", v.Name, v.Actor, v.AuthenticationType, v.IsAuthenticated);

            Console.WriteLine("\n\tClaims from Identity:-");
            foreach (var c in v.Claims)
                Console.WriteLine("\t\tType: {0},\tValue: {1},\tSubject: {2},\tIssuer: {3}", c.Type, c.Value, c.Subject, c.Issuer);
        }

        Console.WriteLine("Claims from other source:-");

        foreach(Claim c in context.User.Claims)
        {
            Console.WriteLine("\t\tType: {0},\tValue: {1},\tSubject: {2},\tIssuer: {3}", c.Type, c.Value, c.Subject, c.Issuer);
        }

        Console.WriteLine("\n *** Starting Authroization. ***\n");

        Claim
            role = context.User.FindFirst("role"),
            accessLevel = context.User.FindFirst("AdminPermission");


        if (role == null)
            Console.WriteLine("\tUser as no 'role' : '{0}'", role == null ? "null" : role.Value);
        else
            Console.WriteLine("\tUser has 'role' : '{0}'", role.Value);

        if (accessLevel == null)
            Console.WriteLine("\tUser has no claim 'AdminPermission' : '{0}'", accessLevel == null ? "null" : accessLevel.Value);
        else
            Console.WriteLine("\tUser has 'AdminPermission' : '{0}'", accessLevel.Value);

        if (role != null && accessLevel != null)
        {
            if (role.Value == "Administrator" && accessLevel.Value == "Read")
                context.Succeed(requirement);
        }
        else
            Console.WriteLine("\n *** Authorization Failue. ***\n");



        return Task.CompletedTask;
    }

}


数据


Data

ApiClaims                       : 
ApiResources                    : api1
ApiScopeClaims                  : AdminPermission, ApiScopeId = 2
ApiScopes                       : api1, ApiResourceId = 1
                                : AdminPermission, ApiResourceId = 1
ApiSecrets                      :
AspNetRoleClaims                : AdminPermission, Read, RoleId = b2f03...
AspNetRoles                     : Administrator, b2f03... 
                                : Customer, 779f7...
                                : Internal, 10d5d...
AspNetUserClaims                : AdminPermission, Create, UserId = 8ee62...
                                : AdminPermission, Update, UserId = 8ee62...
                                : AdminPermission, Delete, UserId = 8ee62...
AspNetUserLogins                :
AspNetUserRoles                 : UserId = 8ee62..., RoleId = b2f03... 
AspNetUsers                     : superuser@mail.com, Id = 8ee62...
AspNetUserTokens                :
ClientClaims                    :
ClientCorsOrigins               :
ClientGrantTypes                : hybrid, ClientId = 1
                                : client_credentials, ClientId = 1
                                : client_credentials, ClientId = 2
                                : password, ClientId = 3
ClientIdPRestrictions           :
ClientPostLogoutRedirectUris    : http://localhost:5002/signout-callback-oidc, ClientId = 1
ClientProperties                :
ClientRedirectUris              : http://localhost:5002/signin-oidc, ClientId = 1
Clients                         : mvc, AllowAccessTokenViaBrowser = 1, AllowOfflineAccess = 1, RequireConsent = 0
                                : client ...
                                : ro.client ...
ClientScopes                    : openid, ClientId = 1
                                : profile, ClientId = 1
                                : AdminPermission, ClientId = 1
ClientSecrets                   : Type = SharedSecret
IdentityClaims                  :   Id  IdentityResourceId  Type
                                    1   1                   sub
                                    2   2                   name
                                    3   2                   family_name
                                    4   2                   given_name
                                    5   2                   middle_name
                                    6   2                   nickname
                                    7   2                   preferred_username
                                    8   2                   profile
                                    9   2                   picture
                                    10  2                   website
                                    11  2                   gender
                                    12  2                   birthdate
                                    13  2                   zoneinfo
                                    14  2                   locale
                                    15  2                   updated_at
                                    16  3                   AdminPermission
IdentityResources               : openid
                                : profile
                                : AdminPermission
PersistedGrants                 : [8x] Type = refresh_token


图片

在AuthorizationHandler开头自动运行


Images

Autos at the beginning of the AuthorizationHandler

身份服务器上显示的声明

在MVC客户端上显示的声明

通过身份验证的用户尝试访问时的日志记录

MVC客户端收到的JWT

数据库表

推荐答案

-编辑-

我已经分叉了您的代码,并解决了该问题.

I've Forked your code and solved the issue.

这是我的仓库的链接.

https://github.com/derekrivers/IdentityServer4

为了修复您的解决方案,我也更改了服务器身份验证响应类型:-

In order to fix your solution, i changed the server authentication response type too :-

"code Id_token"

在您的config.cs中的MVC客户端设置中,我添加了以下属性:-

In the MVC Client setup in your config.cs I added the following properties :-

 AlwaysSendClientClaims = true, 
 AlwaysIncludeUserClaimsInIdToken = true 

我还从mvc客户端中删除了 adminpermission 范围,因为它不是必需的.

I've also removed the adminpermission scope from the mvc client, as it isn't required.

我还稍微修改了 AdminRequirementHandler.cs ,但我将让您在我的存储库中进行探索.

I've also amended the AdminRequirementHandler.cs slightly, but i will let you explore that in my repo.

基本上,我们已经确保用户声明位于Identity令牌中,并且通过这样做,您可以在您的 AdminRequirementHandler

Basically, We have ensured that the user claims are in the Identity token, and by doing this they are then accessible within you AdminRequirementHandler

希望这会有所帮助.

这篇关于IdentityServer4自定义AuthenticationHandler找不到用户的所有声明的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆