基于OpenidConnect规范,角色声明和名称声明的标准类型为role
和name
。但是.net核心System.Security.Claims.ClaimsIdentity.NameClaimType
设置为" http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"并且System.Security.Claims.ClaimsIdentity.RoleClaimType
设置为" http://schemas.microsoft.com/ws/2008/06/identity/claims/role"
我的问题在于角色。
我的ASP.NET核心应用程序正在使用OpenIdConnect进行身份验证。成功进行身份验证后,OpenIdConnect提供程序会将该角色作为声明集合的一部分发回,Cliam.Type
设置为role
,这符合OpenId规范。
但是,由于.Net Core有自己的角色类型,IsInRole()
方法总是返回false。因为我认为IsInRole()
方法使用microsoft的角色类型进行比较。
为什么.net使用不同类型的声明而不是使用标准约定?以及如何解决IsInRole()
问题
更新1
我尝试在启动期间配置声明类型,但它没有用。
startup.cs
public class Startup
{
public Startup(IHostingEnvironment env)
{
// some stuff here that is not related to Identity like building configuration
}
public IConfigurationRoot Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddApplicationInsightsTelemetry(Configuration);
services.AddAuthorization();
services.AddMvc(config =>
{
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
config.Filters.Add(new AuthorizeFilter(policy));
});
// Add Kendo UI services to the services container
services.AddKendo();
// Transform Microsoft cliam types to my claim type
services.AddIdentity<ApplicationUser, ApplicationRole>(options =>
{
options.ClaimsIdentity.RoleClaimType = "role";
options.ClaimsIdentity.UserNameClaimType = "name";
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IApplicationLifetime appLifetime)
{
loggerFactory.AddSerilog();
appLifetime.ApplicationStopped.Register(Log.CloseAndFlush);
app.UseExceptionHandler("/Home/Error");
app.UseApplicationInsightsRequestTelemetry();
app.UseApplicationInsightsExceptionTelemetry();
app.UseStaticFiles();
app.UseIdentityServer(Configuration["Identity:Authority"], Configuration["Identity:ClientId"], Configuration["Identity:PostLogoutRedirectUri"]);
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
// Configure Kendo UI
app.UseKendo(env);
}
}
UseIdentityServer扩展方法
public static void UseIdentityServer(this IApplicationBuilder app, string authority, string clientId, string postlogoutRedirectUri)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions()
{
AutomaticAuthenticate = true,
AutomaticChallenge = true,
AuthenticationScheme = CookieAuthenticationDefaults.AuthenticationScheme,
LoginPath = IdentityConstant.CallbackPath,
AccessDeniedPath = new PathString(IdentityConstant.AccessDeniedPath),
CookieName = IdentityConstant.AuthenticationCookieName,
});
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap = new Dictionary<string, string>();
var connectOptions = new OpenIdConnectOptions()
{
AutomaticChallenge = true,
Authority = authority,
ClientId = clientId,
ResponseType = IdentityConstant.ResponseType,
AuthenticationScheme = IdentityConstant.OpenIdAuthenticationScheme,
SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme,
PostLogoutRedirectUri = postlogoutRedirectUri,
CallbackPath = IdentityConstant.CallbackPath,
Events = new OpenIdConnectEvents()
{
OnTokenValidated = async context =>
{
var userInfoClient = new UserInfoClient(context.Options.Authority + IdentityConstant.UserInfoEndpoint);
var response = await userInfoClient.GetAsync(context.ProtocolMessage.AccessToken);
var claims = response.Claims;
//We will create new identity to store only required claims.
var newIdentity = new ClaimsIdentity(context.Ticket.Principal.Identity.AuthenticationType);
// keep the id_token for logout
newIdentity.AddClaim(new Claim(IdentityConstant.IdTokenClaim, context.ProtocolMessage.IdToken));
// add userinfo claims
newIdentity.AddClaims(claims);
// overwrite existing authentication ticket
context.Ticket = new AuthenticationTicket(
new ClaimsPrincipal(newIdentity),
context.Ticket.Properties,
context.Ticket.AuthenticationScheme);
await Task.FromResult(0);
}
}
};
connectOptions.Scope.Add(IdentityConstant.OpenIdScope);
connectOptions.Scope.Add(IdentityConstant.ProfileScope);
connectOptions.Scope.Add("roles");
app.UseOpenIdConnectAuthentication(connectOptions);
}
更新2
我使用IdentityServer3对所有应用程序进行身份验证。如果使用经典的ASP.NET MVC开发客户端应用程序,则ASP.Net的JWT处理程序会将传入的role
声明类型转换为http://schemas.microsoft.com/ws/2008/06/identity/claims/role
(可以找到更多详细信息{{3在Claims Transformation
部分下)
但是,当使用ASP.NET Core开发客户端应用程序时,情况并非如此。 .net核心不会将声明类型转换为.Net声明类型,这是正确的。但是.Net Core内部使用.Net声明类型来查找用户的角色声明。
这意味着我需要将.Net声明类型转换为所需的声明类型,但不确定在哪里?
答案 0 :(得分:1)
标准惯例是什么?您只是从OpenId Connect规范的上下文中考虑它,而OpenId Connect规范并不是唯一的身份标准。微软已经足够通用以支持所有身份系统。
此处的错误似乎是在OpenId Connect身份验证实现中,因为没有提供使用正确的角色声明类型的ClaimsPrincipal
。
说过你可以通过实施自己的ClaimsPrincipal
来修复它,并覆盖IsInRole()
方法以使用正确的声明类型。
答案 1 :(得分:1)
或者您可以考虑根据OpenId声明的内容,在某个地方放置一些中间件以应用适当的角色声明?
答案 2 :(得分:0)
您可以在应用程序启动期间配置声明类型。
services.AddIdentity<ApplicationUser, IdentityRole>(options => {
options.ClaimsIdentity.RoleClaimType = "http://yourdesiredclaimtype";
options.ClaimsIdentity.UserNameClaimType = "http://yourdesiredclaimtype";
});
您可以在GitHub上看到claim options。