新文章 网摘 文章 随笔 日记

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启动类开始,并添加的设置AntiForgeryConfigUniqueClaimTypeIdentifier设置要求所有基于声明的标识都必须为该声明类型返回唯一值。这将调整反CSRF保护以使用子声明,这是像我们一样设置入站声明类型映射的结果所必需的。

还成立是JwtSecurityTokenHandlerInboundClaimTypeMap通过将其设置为新的字符串和字符串字典,我们实际上是在清除映射器,从而允许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确实很重要。

OWIN客户端要求自动化之前

从初始身份验证返回的声明。

索赔转换

请注意,我们没有收到有关已认证用户的任何声明。要检索这些,我们将需要查询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);
    }
}

注销后,现在将显示以下屏幕,其中包含指向我们客户站点的链接:

OWIN客户索赔

通过将以下属性添加到Identity Server配置中,可以跳过此屏幕IdentityServerOptions

AuthenticationOptions = new AuthenticationOptions { EnablePostSignOutAutoRedirect = true }

您可以使用以下资源阅读有关登出后重定向的更多信息:

最终结果

经过所有这些,我们最终得到以下主张:

OWIN客户索赔

好吧,这就是身份验证了!接下来是有关授权的一些​​基础知识,甚至可能是Web API示例。您可以在我的GitHub存储库中找到此示例客户端中使用的所有源代码

资料来源

posted @ 2020-07-22 11:48  岭南春  阅读(82)  评论(0)    收藏  举报