Identity Server 3独立实施第3部分
不再支持IdentityServer3。我强烈建议您考虑使用IdentityServer4。
在本文中,我们将创建一个混合流客户端,并利用Identity Server和Microsoft Katana OpenID Connect中间件可以提供的某些功能。
混合流
在创建OWIN客户端的同时,我们还将利用这个机会来尝试一下混合流程和一些基本的授权。这将需要对Identity Server实施进行一些更改。
客户
要创建必要的混合客户端,请添加以下客户端配置:
new Client {
ClientId = @"hybridclient",
ClientName = @"Example Hybrid Client",
ClientSecrets = new List<Secret> {
new Secret("idsrv3test".Sha256())
},
Enabled = true,
Flow = Flows.Hybrid,
RequireConsent = true,
AllowRememberConsent = true,
RedirectUris = new List<string> {
"https://localhost:44305/"
},
PostLogoutRedirectUris = new List<string> {
"https://localhost:44305/"
},
AllowedScopes = new List<string> {
Constants.StandardScopes.OpenId,
Constants.StandardScopes.Profile,
Constants.StandardScopes.Email,
Constants.StandardScopes.Roles,
Constants.StandardScopes.OfflineAccess
},
AccessTokenType = AccessTokenType.Jwt
}
您还需要确保将所有新作用域添加到您的作用域存储中,并且您要用来测试它们的用户对每个值都有一个值(显然,这不适用于脱机访问作用域)。
通过添加离线访问范围,我们现在开放了请求刷新令牌的功能。
OWIN客户端和Microsoft Katana OpenID Connect中间件
同样,我们将从一个基本的ASP.NET MVC项目开始,使用空模板并且不使用任何身份验证用户存储。
此解决方案需要以下软件包:
install-package Microsoft.Owin.Host.Systemweb
install-package Microsoft.Owin.Security.Cookies
install-package Microsoft.Owin.Security.OpenIdConnect
install-package IdentityModel
我们将使用与表单发布客户端相同的结构,其中主页显示用户声明,并使用帐户控制器来验证用户身份。但是,与以前的客户端不同,控制器中需要做的就是Signin方法的Authorize属性。这将启动与以前的客户端相同的登录过程,但所需的工作量要少得多。
public class AccountController : Controller {
[Authorize]
public ActionResult SignIn() {
return this.Redirect("/");
}
public ActionResult SignOut() {
this.Request.GetOwinContext().Authentication.SignOut();
return this.Redirect("/");
}
}
如果未登录,则此授权属性将导致401(未授权),我们将要实现的中间件将检测到该消息(302)(已找到),并将其重定向到IdentityServer登录页面。
为简单起见,我们将使用本指南第2部分中相同的视图和部分视图。
启动
我们将以与上一篇文章类似的OWIN启动类开始,并添加的设置AntiForgeryConfig。该UniqueClaimTypeIdentifier设置要求所有基于声明的标识都必须为该声明类型返回唯一值。这将调整反CSRF保护以使用子声明,这是像我们一样设置入站声明类型映射的结果所必需的。
还成立是JwtSecurityTokenHandler的InboundClaimTypeMap。通过将其设置为新的字符串和字符串字典,我们实际上是在清除映射器,从而允许JWT中间件使用Identity Server发送的声明类型,而不是将它们映射到Microsoft XML模式类型(例如,将获得name的,而不是http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name)。
我们还需要使用Cookie OWIN中间件设置应用程序cookie,以允许您实际登录到该应用程序。
public void Configuration(IAppBuilder app) {
AntiForgeryConfig.UniqueClaimTypeIdentifier = "sub";
JwtSecurityTokenHandler.InboundClaimTypeMap = new Dictionary<string, string>();
app.UseCookieAuthentication(new CookieAuthenticationOptions {
AuthenticationType = "Cookies"
});
}
Katana OpenID Connect中间件
Katana中间件负责检测何时需要将用户重定向到OpenID Connect Provider并处理后续交互。然后,该中间件将使我们登录到我们的应用程序。该方法的基本实现相对来说是不言自明的,具有您已经看到的概念:
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions {
ClientId = "hybridclient",
Authority = IdServBaseUri,
RedirectUri = ClientUri,
PostLogoutRedirectUri = ClientUri,
ResponseType = "code id_token token",
Scope = "openid profile email roles offline_access",
TokenValidationParameters = new TokenValidationParameters {
NameClaimType = "name",
RoleClaimType = "role"
},
SignInAsAuthenticationType = "Cookies"
});
这个简单的启动类使我们可以通过我们的OpenID Connect Provider进行身份验证。Katana OpenID Connect中间件正在使用您可以在.well-known/openid-configuration端点上找到的信息进行自我配置。它需要我们提供的是OpenID Connect Provider基本URL,客户端ID,重定向URL,范围和响应类型。当我们使用它时,我们告诉cookie使用我们的OpenID Connect声明类型作为名称和角色声明。容易吗?
请注意,您设置TokenValidationParameters和的顺序SignInAsAuthenticationType确实很重要。

从初始身份验证返回的声明。
索赔转换
请注意,我们没有收到有关已认证用户的任何声明。要检索这些,我们将需要查询OpenID Connect UserInfo端点。您可能还注意到,我们还显示了许多怪异而奇妙的声明,客户应用程序或用户不需要关心这些声明。
使用Katana中间件,我们可以轻松地映射或删除传入的声明,从而通过声明转换来使事情更具可读性和简洁性,并同时请求有关经过身份验证的用户的一些信息。当我们收到已收到授权码的通知后,便会执行此操作。这是通过Notifications使用OpenIdConnectAuthenticationNotifications类的实现设置属性并拦截AuthorizationCodeReceived事件来完成的。
Notifications = new OpenIdConnectAuthenticationNotifications {
AuthorizationCodeReceived = async n => {
var userInfoClient = new UserInfoClient(new Uri(UserInfoEndpoint), n.ProtocolMessage.AccessToken);
var userInfoResponse = await userInfoClient.GetAsync();
var identity = new ClaimsIdentity(n.AuthenticationTicket.Identity.AuthenticationType);
identity.AddClaims(userInfoResponse.GetClaimsIdentity().Claims);
n.AuthenticationTicket = new AuthenticationTicket(identity, n.AuthenticationTicket.Properties);
}
}
在这里,我们使用UserInfoClientIdentityModel库中的帮助程序来检索用户的声明,然后创建一个新的ClaimsIdentity,保持AuthenticationType不变。然后,我们使用从现有ClaimsIdentity以及我们从userinfo端点收到的所有声明的所有声明填充此身份。然后,我们可以将这些声明存储在我们的身份验证Cookie中。
刷新令牌
为了增加对长期登录的支持,我们需要从授权服务器获取刷新令牌。当当前令牌变为无效或过期时,此刷新令牌可用于获取新的访问令牌。有关刷新令牌的更多详细信息,请参见OAuth 2.0规范。
我们可以在授权代码回调中再次使用Katana OIDC中间件来包括对刷新令牌的支持。
var tokenClient = new TokenClient(TokenEndpoint, "hybridclient", "idsrv3test");
var response = await tokenClient.RequestAuthorizationCodeAsync(n.Code, n.RedirectUri);
identity.AddClaim(new Claim("access_token", response.AccessToken));
identity.AddClaim(new Claim("expires_at", DateTime.UtcNow.AddSeconds(response.ExpiresIn).ToLocalTime().ToString(CultureInfo.InvariantCulture)));
identity.AddClaim(new Claim("refresh_token", response.RefreshToken));
在这里,我们这次使用IdentityModel库中的另一个帮助程序类TokenClient。这用于OAuth请求的简单处理,并且在内部是包装器HttpClient。通过像这样交易我们的授权码,我们将收到一个新的访问令牌和一个刷新令牌。
发布注销重定向
我们已经在Identity Server实现中设置了注销后重定向URL,您可能已经注意到,在OpenID Connect结束会话终结点中可以使用类似的参数。要使用注销后重定向,我们还应该提供先前发行的ID令牌,以便为OpenID Connect提供程序提供有关当前已认证会话的某种想法。
我们可以使用Katana OIDC中间件来实现此目的,方法是先保留传入的令牌,然后将其附加到注销请求。要保留ID令牌,我们需要将其包含在AuthorizationCodeReceived通知的声明转换中。
nIdentity.AddClaim(new Claim("id_token", n.ProtocolMessage.IdToken));
现在,我们可以在注销后传递身份令牌。可以使用如下所示的RedirectToIdentityProvider通知完成此操作:
RedirectToIdentityProvider = n => {
if (n.ProtocolMessage.RequestType == OpenIdConnectRequestType.LogoutRequest)
{
var idTokenHint = n.OwinContext.Authentication.User.FindFirst("id_token").Value;
n.ProtocolMessage.IdTokenHint = idTokenHint;
return Task.FromResult(0);
}
}
注销后,现在将显示以下屏幕,其中包含指向我们客户站点的链接:

通过将以下属性添加到Identity Server配置中,可以跳过此屏幕IdentityServerOptions:
AuthenticationOptions = new AuthenticationOptions { EnablePostSignOutAutoRedirect = true }
您可以使用以下资源阅读有关登出后重定向的更多信息:
- OpenID Connect会话管理1.0(草案26) -用于会话管理的OpenId Connect规范草案,包括对结束会话端点的详细描述。
- IdentityServer v3和“发布注销重定向” -Dominick Baier博客中有关发布注销重定向以及为什么Identity Server 3不允许自动重定向的博文。
最终结果
经过所有这些,我们最终得到以下主张:

好吧,这就是身份验证了!接下来是有关授权的一些基础知识,甚至可能是Web API示例。您可以在我的GitHub存储库中找到此示例客户端中使用的所有源代码。
资料来源
- OWIN Katana客户端源代码 -此博客文章中演示的OWIN Katana客户端的源代码
- IdentityServer实现源代码 -此客户端使用的IdentityServer实现的源代码
- OpenID Connect混合流和IdentityServer v3 -Dominick Baier博客文章关于将混合流与IdentityServer v3一起使用
浙公网安备 33010602011771号