我正在使用使用Asp.Net Identity和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
Autos at the beginning of the AuthorizationHandler
Claims shown on the identity server
Claims shows on the MVC client
答案 0 :(得分:1)
<强> - 编辑 - 强>
我已经分解了你的代码并解决了这个问题。
这是我的回购链接。
https://github.com/derekrivers/IdentityServer4
为了修复您的解决方案,我也更改了服务器身份验证响应类型: -
"code Id_token"
在config.cs的MVC Client设置中,我添加了以下属性: -
AlwaysSendClientClaims = true,
AlwaysIncludeUserClaimsInIdToken = true
我还从mvc客户端删除了 adminpermission 范围,因为它不是必需的。
我还稍微修改了 AdminRequirementHandler.cs ,但我会让你在我的回购中探索它。
基本上,我们确保用户声明属于身份令牌,然后通过这样做可以在 AdminRequirementHandler
中访问它们希望这有帮助。