从 Core 1.1 迁移到 2.0 - 身份验证
Migration from Core 1.1 to 2.0 - Authentication
在我的 .net 核心 1.1 代码中,我按如下方式进行身份验证(将不记名令牌发送到外部 URL 并在 return 令牌中查找声明)。此代码在 Configure
方法中
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationScheme = "Cookies"
});
app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions
{
AuthenticationScheme = "oidc",
SignInScheme = "Cookies",
Authority = signinAuthority,
RequireHttpsMetadata = signinHTTPS,
ClientId = "skybus",
ClientSecret = "secret",
ResponseType = "code id_token",
Scope = { "api1", "offline_access" },
GetClaimsFromUserInfoEndpoint = true,
SaveTokens = true
});
现在我将代码升级到 .net Core 2.0,UseCookieAuthentication
和 UseOpenIdConnectAuthentication
都发生了变化。我发现很难找到在这种情况下需要做什么
我在ConfigureServices
方法中改成如下
services.AddAuthentication(options => {
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie()
.AddOpenIdConnect(o =>
{
o.Authority = signinAuthority;
o.SignInScheme = "Cookies";
o.RequireHttpsMetadata = signinHTTPS;
o.ClientId = "skybus";
o.ClientSecret = "secret";
o.ResponseType = "code id_token";
o.GetClaimsFromUserInfoEndpoint = true;
o.SaveTokens = true;
o.Scope.Add("api1");
o.Scope.Add("offline_access");
});
在浏览器中,我在上述更改后看到 URL。如果用户未登录,它应该向我显示外部登录页面,或者 return 到我网站的主页
我不知道您是否在使用 IdentityServer4,但他们在 github 上为 ASP.NET Core 2.0 提供了一些示例。
希望对您有所帮助。
你使用认证中间件了吗?
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseAuthentication();
...
我按照 this link from microsoft 开始迁移。大多数迁移都包含在这个 link 中,但我遇到了我的大部分声明都丢失的问题。
使用 ASP.NET 核心 1.x,客户端会收到声明:nbf、exp、iss、aud、nonce、iat、c_hash、sid、sub、auth_time, 国内流离失所者, amr.
在 Core 2.0 中我们只得到 sid、sub 和 idp。发生什么事了?
Microsoft 在其 OpenID Connect 处理程序中添加了一个名为 ClaimActions 的新概念。声明操作允许修改来自外部提供者的声明如何映射(或不映射)到您的 ClaimsPrincipal 中的声明。查看 OpenIdConnectOptions 的构造函数,您可以看到处理程序现在将默认跳过以下声明:
ClaimActions.DeleteClaim("nonce");
ClaimActions.DeleteClaim("aud");
ClaimActions.DeleteClaim("azp");
ClaimActions.DeleteClaim("acr");
ClaimActions.DeleteClaim("amr");
ClaimActions.DeleteClaim("iss");
ClaimActions.DeleteClaim("iat");
ClaimActions.DeleteClaim("nbf");
ClaimActions.DeleteClaim("exp");
ClaimActions.DeleteClaim("at_hash");
ClaimActions.DeleteClaim("c_hash");
ClaimActions.DeleteClaim("auth_time");
ClaimActions.DeleteClaim("ipaddr");
ClaimActions.DeleteClaim("platf");
ClaimActions.DeleteClaim("ver");
如果您想“取消跳过”索赔,您需要在设置处理程序时删除特定的索赔操作。以下是获取 amr 声明的非常直观的语法:
options.ClaimActions.Remove("amr");
向 OIDC 提供商请求更多声明
当您请求更多范围时,例如导致更多声明的配置文件或自定义范围,还有另一个需要注意的令人困惑的细节。
根据 OIDC 协议中的 response_type,一些声明通过 id_token 传输,一些通过 userinfo 端点传输。
所以首先,您需要在处理程序中启用对 userinfo 端点的支持:
options.GetClaimsFromUserInfoEndpoint = true;
最后您需要添加以下内容class以导入所有其他自定义声明
public class MapAllClaimsAction : ClaimAction
{
public MapAllClaimsAction() : base(string.Empty, string.Empty)
{
}
public override void Run(JObject userData, ClaimsIdentity identity, string issuer)
{
foreach (var claim in identity.Claims)
{
// If this claimType is mapped by the JwtSeurityTokenHandler, then this property will be set
var shortClaimTypeName = claim.Properties.ContainsKey(JwtSecurityTokenHandler.ShortClaimTypeProperty) ?
claim.Properties[JwtSecurityTokenHandler.ShortClaimTypeProperty] : string.Empty;
// checking if claim in the identity (generated from id_token) has the same type as a claim retrieved from userinfo endpoint
JToken value;
var isClaimIncluded = userData.TryGetValue(claim.Type, out value) || userData.TryGetValue(shortClaimTypeName, out value);
// if a same claim exists (matching both type and value) both in id_token identity and userinfo response, remove the json entry from the userinfo response
if (isClaimIncluded && claim.Value.Equals(value.ToString(), StringComparison.Ordinal))
{
if (!userData.Remove(claim.Type))
{
userData.Remove(shortClaimTypeName);
}
}
}
// adding remaining unique claims from userinfo endpoint to the identity
foreach (var pair in userData)
{
JToken value;
var claimValue = userData.TryGetValue(pair.Key, out value) ? value.ToString() : null;
identity.AddClaim(new Claim(pair.Key, claimValue, ClaimValueTypes.String, issuer));
}
}
}
然后使用上面的class代码将其添加到ClaimActions
options.ClaimActions.Add(new MapAllClaimsAction());
在我的 .net 核心 1.1 代码中,我按如下方式进行身份验证(将不记名令牌发送到外部 URL 并在 return 令牌中查找声明)。此代码在 Configure
方法中
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationScheme = "Cookies"
});
app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions
{
AuthenticationScheme = "oidc",
SignInScheme = "Cookies",
Authority = signinAuthority,
RequireHttpsMetadata = signinHTTPS,
ClientId = "skybus",
ClientSecret = "secret",
ResponseType = "code id_token",
Scope = { "api1", "offline_access" },
GetClaimsFromUserInfoEndpoint = true,
SaveTokens = true
});
现在我将代码升级到 .net Core 2.0,UseCookieAuthentication
和 UseOpenIdConnectAuthentication
都发生了变化。我发现很难找到在这种情况下需要做什么
我在ConfigureServices
方法中改成如下
services.AddAuthentication(options => {
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie()
.AddOpenIdConnect(o =>
{
o.Authority = signinAuthority;
o.SignInScheme = "Cookies";
o.RequireHttpsMetadata = signinHTTPS;
o.ClientId = "skybus";
o.ClientSecret = "secret";
o.ResponseType = "code id_token";
o.GetClaimsFromUserInfoEndpoint = true;
o.SaveTokens = true;
o.Scope.Add("api1");
o.Scope.Add("offline_access");
});
在浏览器中,我在上述更改后看到 URL。如果用户未登录,它应该向我显示外部登录页面,或者 return 到我网站的主页
我不知道您是否在使用 IdentityServer4,但他们在 github 上为 ASP.NET Core 2.0 提供了一些示例。
希望对您有所帮助。
你使用认证中间件了吗?
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseAuthentication();
...
我按照 this link from microsoft 开始迁移。大多数迁移都包含在这个 link 中,但我遇到了我的大部分声明都丢失的问题。
使用 ASP.NET 核心 1.x,客户端会收到声明:nbf、exp、iss、aud、nonce、iat、c_hash、sid、sub、auth_time, 国内流离失所者, amr.
在 Core 2.0 中我们只得到 sid、sub 和 idp。发生什么事了?
Microsoft 在其 OpenID Connect 处理程序中添加了一个名为 ClaimActions 的新概念。声明操作允许修改来自外部提供者的声明如何映射(或不映射)到您的 ClaimsPrincipal 中的声明。查看 OpenIdConnectOptions 的构造函数,您可以看到处理程序现在将默认跳过以下声明:
ClaimActions.DeleteClaim("nonce");
ClaimActions.DeleteClaim("aud");
ClaimActions.DeleteClaim("azp");
ClaimActions.DeleteClaim("acr");
ClaimActions.DeleteClaim("amr");
ClaimActions.DeleteClaim("iss");
ClaimActions.DeleteClaim("iat");
ClaimActions.DeleteClaim("nbf");
ClaimActions.DeleteClaim("exp");
ClaimActions.DeleteClaim("at_hash");
ClaimActions.DeleteClaim("c_hash");
ClaimActions.DeleteClaim("auth_time");
ClaimActions.DeleteClaim("ipaddr");
ClaimActions.DeleteClaim("platf");
ClaimActions.DeleteClaim("ver");
如果您想“取消跳过”索赔,您需要在设置处理程序时删除特定的索赔操作。以下是获取 amr 声明的非常直观的语法:
options.ClaimActions.Remove("amr");
向 OIDC 提供商请求更多声明
当您请求更多范围时,例如导致更多声明的配置文件或自定义范围,还有另一个需要注意的令人困惑的细节。
根据 OIDC 协议中的 response_type,一些声明通过 id_token 传输,一些通过 userinfo 端点传输。
所以首先,您需要在处理程序中启用对 userinfo 端点的支持:
options.GetClaimsFromUserInfoEndpoint = true;
最后您需要添加以下内容class以导入所有其他自定义声明
public class MapAllClaimsAction : ClaimAction
{
public MapAllClaimsAction() : base(string.Empty, string.Empty)
{
}
public override void Run(JObject userData, ClaimsIdentity identity, string issuer)
{
foreach (var claim in identity.Claims)
{
// If this claimType is mapped by the JwtSeurityTokenHandler, then this property will be set
var shortClaimTypeName = claim.Properties.ContainsKey(JwtSecurityTokenHandler.ShortClaimTypeProperty) ?
claim.Properties[JwtSecurityTokenHandler.ShortClaimTypeProperty] : string.Empty;
// checking if claim in the identity (generated from id_token) has the same type as a claim retrieved from userinfo endpoint
JToken value;
var isClaimIncluded = userData.TryGetValue(claim.Type, out value) || userData.TryGetValue(shortClaimTypeName, out value);
// if a same claim exists (matching both type and value) both in id_token identity and userinfo response, remove the json entry from the userinfo response
if (isClaimIncluded && claim.Value.Equals(value.ToString(), StringComparison.Ordinal))
{
if (!userData.Remove(claim.Type))
{
userData.Remove(shortClaimTypeName);
}
}
}
// adding remaining unique claims from userinfo endpoint to the identity
foreach (var pair in userData)
{
JToken value;
var claimValue = userData.TryGetValue(pair.Key, out value) ? value.ToString() : null;
identity.AddClaim(new Claim(pair.Key, claimValue, ClaimValueTypes.String, issuer));
}
}
}
然后使用上面的class代码将其添加到ClaimActions
options.ClaimActions.Add(new MapAllClaimsAction());