繁体   English   中英

IdentityServer4自定义AuthenticationHandler找不到用户的所有声明

[英]IdentityServer4 custom AuthenticationHandler can't find all claims for a user

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

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

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

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

似乎没有一种简单的方法来检查“ AspNetClaims”中的声明,就像“ IdentityClaims”中的声明一样,因此我认为我在某个地方出错了。

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

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

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


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

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

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

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

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

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

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

}

数据

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的开头自动运行

身份服务器上显示的声明

索赔显示在MVC客户端上

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

MVC客户端收到JWT

数据库表

- 编辑 -

我已经分叉了您的代码并解决了问题。

这是我的仓库的链接。

https://github.com/derekrivers/IdentityServer4

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

"code Id_token"

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

 AlwaysSendClientClaims = true, 
 AlwaysIncludeUserClaimsInIdToken = true 

我还从mvc客户端中删除了管理员权限范围,因为它不是必需的。

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

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

希望这可以帮助。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM