无法在 .NET Core 2.0 中检索声明
Unable to Retrieve Claims in .NET Core 2.0
我在 .NET Core 1.1 上使用 OpenId Connect 身份验证服务器,特别是 Identity Server 4(版本 1.5.2)。我有这个 运行 ASP.NET Framework MVC 5 和 ASP.NET Core 1.1 MVC Web 应用程序。以下配置来自 .NET Core 1.1 Web 应用程序:
public void Configure(
IApplicationBuilder app,
IHostingEnvironment env,
ILoggerFactory loggerFactory)
{
app.UseDeveloperExceptionPage();
app.UseStatusCodePages();
app.UseRewriter(new RewriteOptions().AddRedirectToHttps());
app.UseStaticFiles();
#region Configure Authentication
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
app.UseCookieAuthentication(
new CookieAuthenticationOptions
{
AuthenticationScheme = "Cookies",
AutomaticAuthenticate = true,
AccessDeniedPath = "/AccessDenied"
});
app.UseOpenIdConnectAuthentication(
new OpenIdConnectOptions
{
AuthenticationScheme = "oidc",
SignInScheme = "Cookies",
Authority = "https://localhost:44316",
ClientId = "test-mule",
ClientSecret = "secret",
ResponseType = "code id_token",
SaveTokens = true,
GetClaimsFromUserInfoEndpoint = true,
PostLogoutRedirectUri = "https://localhost:44324",
RequireHttpsMetadata = true,
Scope = { "openid", "profile", "name", "email", "org", "role" },
TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = "name",
RoleClaimType = "role"
}
});
#endregion Configure Authentication
app.UseMvc();
}
登录后,我可以列出与经过身份验证的用户关联的声明:
var claims = User.Claims.OrderBy(c => c.Type).ToList();
在 ASP.NET 1.1 应用程序中,这提供了以下声明列表:
amr pwd
aud test-mule
auth_time 1504529067
c_hash nouhsuXtd5iKT7B33zxkxg
email tom@
exp 1504532668
family_name Cobley
given_name Tom
iat 1504529068
idp local
iss https://localhost:44316
name tom
nbf 1504529068
nonce 6364012...
org IBX
role SysAdmin
role TeleMarketing
role AccountManager
role DataManager
role Member
sid 2091...
sub 1b19...440fa
这就是我want/expect.
我现在正尝试在我的第一个 ASP.NET Core 2.0 应用程序中使用以下 Startup
配置复制此行为:
public Startup()
{
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
}
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication("Cookies")
.AddCookie("Cookies", options =>
{
options.LoginPath = "/SignIn";
options.AccessDeniedPath = "/AccessDenied";
})
.AddOpenIdConnect("oidc", options =>
{
options.SignInScheme = "Cookies";
options.Authority = "https://localhost:44316";
options.ClientId = "test-mule";
options.ClientSecret = "secret";
options.ResponseType = "code id_token";
options.SaveTokens = true;
options.GetClaimsFromUserInfoEndpoint = true;
options.SignedOutRedirectUri = "https://localhost:44367";
options.RequireHttpsMetadata = true;
options.Scope.Clear();
options.Scope.Add("openid");
options.Scope.Add("profile");
options.Scope.Add("name");
options.Scope.Add("email");
options.Scope.Add("org");
options.Scope.Add("role");
options.TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = "name",
RoleClaimType = "role"
};
});
// Add framework services.
services.AddMvc();
}
作为参考,/Signin
控制器操作如下所示:
[Route("/SignIn")]
public IActionResult SignIn(string returnUrl = null)
{
if (!Url.IsLocalUrl(returnUrl)) returnUrl = "/";
var props = new AuthenticationProperties
{
RedirectUri = returnUrl
};
return Challenge(props, "oidc");
}
在此环境中,成功登录后,如果我列出用户的声明,我只会看到 Core 1.1 中可用内容的子集:
email tom@...
family_name Cobley
given_name Tom
idp local
name tom.cobley
sid 2091...
sub 1b19...440fa
我在客户端和服务器上都有 运行 跟踪日志,但不能 see/identify 任何不愉快的事情)。我还假设这不是 Identity Server 问题,因为它是 'just' 应该与任何客户端保持一致的 Open Id Connect 服务?
任何人都可以指出我哪里出错的正确方向吗?
谢谢。
更新
根据 MVCutter 的建议,我添加了一个 `OnUserInformationReceived 事件处理程序,因为我注意到并非所有自定义声明都正确映射到用户身份。我不确定为什么这是必要的,或者是否有更好的地方可以做到这一点,但它似乎给了我现在想要的东西。
private Task OnUserInformationReceivedHandler(
UserInformationReceivedContext context)
{
if (!(context.Principal.Identity is ClaimsIdentity claimsId))
{
throw new Exception();
}
// Get a list of all claims attached to the UserInformationRecieved context
var ctxClaims = context.User.Children().ToList();
foreach (var ctxClaim in ctxClaims)
{
var claimType = ctxClaim.Path;
var token = ctxClaim.FirstOrDefault();
if (token == null)
{
continue;
}
var claims = new List<Claim>();
if (token.Children().Any())
{
claims.AddRange(
token.Children()
.Select(c => new Claim(claimType, c.Value<string>())));
}
else
{
claims.Add(new Claim(claimType, token.Value<string>()));
}
foreach (var claim in claims)
{
if (!claimsId.Claims.Any(
c => c.Type == claim.Type &&
c.Value == claim.Value))
{
claimsId.AddClaim(claim);
}
}
}
return Task.CompletedTask;
}
请看下面,我运行遇到了和你一样的问题。我确定我们遗漏了一个配置问题,但目前我在 OnUserInformationReceived 事件处理程序中解析了从 UserInfoEndpoint 返回的值。
public override void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(sharedOptions =>
{
sharedOptions.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
sharedOptions.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
sharedOptions.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie()
.AddOpenIdConnect(o =>
{
o.ClientId = "sss";
o.ClientSecret = "sss";
o.RequireHttpsMetadata = false;
o.Authority = "http://localhost:60000/";
o.MetadataAddress = "http://localhost:60000/IdSrv/.well-known/openid-configuration";
o.ResponseType = OpenIdConnectResponseType.IdTokenToken;
o.CallbackPath = new PathString("/CGI/Home/Index");
o.SignedOutCallbackPath = new PathString("/CGI/Account/LoggedOut");
o.Scope.Add("openid");
o.Scope.Add("roles");
o.SaveTokens = true;
o.GetClaimsFromUserInfoEndpoint = true;
o.Events = new OpenIdConnectEvents()
{
OnUserInformationReceived = (context) =>
{
ClaimsIdentity claimsId = context.Principal.Identity as ClaimsIdentity;
var roles = context.User.Children().FirstOrDefault(j => j.Path == JwtClaimTypes.Role).Values().ToList();
claimsId.AddClaims(roles.Select(r => new Claim(JwtClaimTypes.Role, r.Value<String>())));
return Task.FromResult(0);
}
};
o.TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = JwtClaimTypes.Name,
RoleClaimType = JwtClaimTypes.Role,
};
});
}
编辑:
我发现 ClaimsAction 属性 有一个名为 MapUniqueJsonKey 的扩展方法,这似乎适用于自定义单值键,但在数组类型(如角色)上炸弹...越来越近
o.ClaimActions.MapUniqueJsonKey("UserType", "UserType");
ASP.NET Core 2 向 OpenIdConnectionOptions 引入了 ClaimActions 属性。 ClaimActions 的默认集合将删除您要查找的声明。您可以通过清除选项对象上的 ClaimActions 取回声明:
options.ClaimActions.Clear();
另请参阅:https://leastprivilege.com/2017/11/15/missing-claims-in-the-asp-net-core-2-openid-connect-handler/
我在 .NET Core 1.1 上使用 OpenId Connect 身份验证服务器,特别是 Identity Server 4(版本 1.5.2)。我有这个 运行 ASP.NET Framework MVC 5 和 ASP.NET Core 1.1 MVC Web 应用程序。以下配置来自 .NET Core 1.1 Web 应用程序:
public void Configure(
IApplicationBuilder app,
IHostingEnvironment env,
ILoggerFactory loggerFactory)
{
app.UseDeveloperExceptionPage();
app.UseStatusCodePages();
app.UseRewriter(new RewriteOptions().AddRedirectToHttps());
app.UseStaticFiles();
#region Configure Authentication
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
app.UseCookieAuthentication(
new CookieAuthenticationOptions
{
AuthenticationScheme = "Cookies",
AutomaticAuthenticate = true,
AccessDeniedPath = "/AccessDenied"
});
app.UseOpenIdConnectAuthentication(
new OpenIdConnectOptions
{
AuthenticationScheme = "oidc",
SignInScheme = "Cookies",
Authority = "https://localhost:44316",
ClientId = "test-mule",
ClientSecret = "secret",
ResponseType = "code id_token",
SaveTokens = true,
GetClaimsFromUserInfoEndpoint = true,
PostLogoutRedirectUri = "https://localhost:44324",
RequireHttpsMetadata = true,
Scope = { "openid", "profile", "name", "email", "org", "role" },
TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = "name",
RoleClaimType = "role"
}
});
#endregion Configure Authentication
app.UseMvc();
}
登录后,我可以列出与经过身份验证的用户关联的声明:
var claims = User.Claims.OrderBy(c => c.Type).ToList();
在 ASP.NET 1.1 应用程序中,这提供了以下声明列表:
amr pwd
aud test-mule
auth_time 1504529067
c_hash nouhsuXtd5iKT7B33zxkxg
email tom@
exp 1504532668
family_name Cobley
given_name Tom
iat 1504529068
idp local
iss https://localhost:44316
name tom
nbf 1504529068
nonce 6364012...
org IBX
role SysAdmin
role TeleMarketing
role AccountManager
role DataManager
role Member
sid 2091...
sub 1b19...440fa
这就是我want/expect.
我现在正尝试在我的第一个 ASP.NET Core 2.0 应用程序中使用以下 Startup
配置复制此行为:
public Startup()
{
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
}
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication("Cookies")
.AddCookie("Cookies", options =>
{
options.LoginPath = "/SignIn";
options.AccessDeniedPath = "/AccessDenied";
})
.AddOpenIdConnect("oidc", options =>
{
options.SignInScheme = "Cookies";
options.Authority = "https://localhost:44316";
options.ClientId = "test-mule";
options.ClientSecret = "secret";
options.ResponseType = "code id_token";
options.SaveTokens = true;
options.GetClaimsFromUserInfoEndpoint = true;
options.SignedOutRedirectUri = "https://localhost:44367";
options.RequireHttpsMetadata = true;
options.Scope.Clear();
options.Scope.Add("openid");
options.Scope.Add("profile");
options.Scope.Add("name");
options.Scope.Add("email");
options.Scope.Add("org");
options.Scope.Add("role");
options.TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = "name",
RoleClaimType = "role"
};
});
// Add framework services.
services.AddMvc();
}
作为参考,/Signin
控制器操作如下所示:
[Route("/SignIn")]
public IActionResult SignIn(string returnUrl = null)
{
if (!Url.IsLocalUrl(returnUrl)) returnUrl = "/";
var props = new AuthenticationProperties
{
RedirectUri = returnUrl
};
return Challenge(props, "oidc");
}
在此环境中,成功登录后,如果我列出用户的声明,我只会看到 Core 1.1 中可用内容的子集:
email tom@...
family_name Cobley
given_name Tom
idp local
name tom.cobley
sid 2091...
sub 1b19...440fa
我在客户端和服务器上都有 运行 跟踪日志,但不能 see/identify 任何不愉快的事情)。我还假设这不是 Identity Server 问题,因为它是 'just' 应该与任何客户端保持一致的 Open Id Connect 服务?
任何人都可以指出我哪里出错的正确方向吗?
谢谢。
更新
根据 MVCutter 的建议,我添加了一个 `OnUserInformationReceived 事件处理程序,因为我注意到并非所有自定义声明都正确映射到用户身份。我不确定为什么这是必要的,或者是否有更好的地方可以做到这一点,但它似乎给了我现在想要的东西。
private Task OnUserInformationReceivedHandler(
UserInformationReceivedContext context)
{
if (!(context.Principal.Identity is ClaimsIdentity claimsId))
{
throw new Exception();
}
// Get a list of all claims attached to the UserInformationRecieved context
var ctxClaims = context.User.Children().ToList();
foreach (var ctxClaim in ctxClaims)
{
var claimType = ctxClaim.Path;
var token = ctxClaim.FirstOrDefault();
if (token == null)
{
continue;
}
var claims = new List<Claim>();
if (token.Children().Any())
{
claims.AddRange(
token.Children()
.Select(c => new Claim(claimType, c.Value<string>())));
}
else
{
claims.Add(new Claim(claimType, token.Value<string>()));
}
foreach (var claim in claims)
{
if (!claimsId.Claims.Any(
c => c.Type == claim.Type &&
c.Value == claim.Value))
{
claimsId.AddClaim(claim);
}
}
}
return Task.CompletedTask;
}
请看下面,我运行遇到了和你一样的问题。我确定我们遗漏了一个配置问题,但目前我在 OnUserInformationReceived 事件处理程序中解析了从 UserInfoEndpoint 返回的值。
public override void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(sharedOptions =>
{
sharedOptions.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
sharedOptions.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
sharedOptions.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie()
.AddOpenIdConnect(o =>
{
o.ClientId = "sss";
o.ClientSecret = "sss";
o.RequireHttpsMetadata = false;
o.Authority = "http://localhost:60000/";
o.MetadataAddress = "http://localhost:60000/IdSrv/.well-known/openid-configuration";
o.ResponseType = OpenIdConnectResponseType.IdTokenToken;
o.CallbackPath = new PathString("/CGI/Home/Index");
o.SignedOutCallbackPath = new PathString("/CGI/Account/LoggedOut");
o.Scope.Add("openid");
o.Scope.Add("roles");
o.SaveTokens = true;
o.GetClaimsFromUserInfoEndpoint = true;
o.Events = new OpenIdConnectEvents()
{
OnUserInformationReceived = (context) =>
{
ClaimsIdentity claimsId = context.Principal.Identity as ClaimsIdentity;
var roles = context.User.Children().FirstOrDefault(j => j.Path == JwtClaimTypes.Role).Values().ToList();
claimsId.AddClaims(roles.Select(r => new Claim(JwtClaimTypes.Role, r.Value<String>())));
return Task.FromResult(0);
}
};
o.TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = JwtClaimTypes.Name,
RoleClaimType = JwtClaimTypes.Role,
};
});
}
编辑:
我发现 ClaimsAction 属性 有一个名为 MapUniqueJsonKey 的扩展方法,这似乎适用于自定义单值键,但在数组类型(如角色)上炸弹...越来越近
o.ClaimActions.MapUniqueJsonKey("UserType", "UserType");
ASP.NET Core 2 向 OpenIdConnectionOptions 引入了 ClaimActions 属性。 ClaimActions 的默认集合将删除您要查找的声明。您可以通过清除选项对象上的 ClaimActions 取回声明:
options.ClaimActions.Clear();
另请参阅:https://leastprivilege.com/2017/11/15/missing-claims-in-the-asp-net-core-2-openid-connect-handler/