IdentityServer4 快速入门#3: 使用OpenID Connect隐式流程进行用户身份验证

注意

对于任何先决条件(例如模板),请先查看概述。
在本快速入门中,我们希望通过OpenID Connect协议向我们的IdentityServer添加对交互式用户身份验证的支持。
安装好之后,我们将创建一个将使用IdentityServer进行身份验证的MVC应用程序。

添加UI

Identity Server已内置OpenID Connect所需的所有协议支持。 您需要提供登录,注销,同意和错误所需的UI部分。

虽然外观和确切的工作流程在每个IdentityServer实施中可能总是会有所不同,但我们提供了一个基于MVC的示例UI,您可以将其用作起点。

您可以在快速入门用户界面存储库中找到该用户界面。 您可以克隆或下载此存储库,然后将控制器,视图,模型和CSS放到IdentityServer Web应用程序中。

或者,您可以使用.NET CLI(从src/IdentityServer文件夹中运行):

dotnet new is4ui

添加MVC UI后,还需要在DI系统和管道中启用MVC。 当您查看Startup.cs时,您会在ConfigureServicesConfigure方法中找到注释,这些注释告诉您如何启用MVC。

运行IdentityServer应用程序,您现在应该看到一个主页。

花一些时间检查控制器和模型,您对它们的了解越深入,将来进行修改的难度就越大。 大多数代码使用“功能文件夹”样式保存在“快速入门”文件夹中。 如果这种风格不适合您,请随时以所需的任何方式组织代码。

创建一个MVC客户端

接下来,您将在解决方案中添加一个MVC应用程序。 为此,请使用ASP.NET Core“ Web应用程序”(即MvcClient)模板。 不要在向导中配置“Authentication”设置-您将在此快速入门中手动进行设置。 创建项目后,将应用程序配置为在端口5002上运行。

要将对OpenID Connect身份验证的支持添加到MVC应用程序,请在Startup.cs中的ConfigureServices中添加以下内容:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();

    JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();

    services.AddAuthentication(options =>
        {
            options.DefaultScheme = "Cookies";
            options.DefaultChallengeScheme = "oidc";
        })
        .AddCookie("Cookies")
        .AddOpenIdConnect("oidc", options =>
        {
            options.Authority = "http://localhost:5000";
            options.RequireHttpsMetadata = false;

            options.ClientId = "mvc";
            options.SaveTokens = true;
        });
}

AddAuthentication将身份验证服务添加到DI。 我们使用cookie来本地登录用户(通过“Cookies作为DefaultScheme),并且将DefaultChallengeScheme设置为oidc,因为当需要用户登录时,我们将使用OpenID Connect协议。

然后,我们使用AddCookie添加可以处理cookie的处理程序。

最后,AddOpenIdConnect用于配置执行OpenID Connect协议的处理程序。 Authority表明我们信任IdentityServer。 然后,我们通过ClientId识别此客户端。 SaveTokens用于将IdentityServer中的令牌保留在cookie中(稍后将需要它们)。

同样,我们已关闭JWT声明类型映射,以允许众所周知的声明(例如“ sub”和“ idp”)不受干扰地流过:

JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();

然后,要确保身份验证服务对每个请求执行,在Startup.csd文件中的Configure方法添加 UseAuthentication:

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
    }

    app.UseAuthentication();

    app.UseStaticFiles();
    app.UseMvcWithDefaultRoute();
}

认证中间件应在管道中的MVC之前添加。

最后一步是触发身份验证握手。 为此,转到Home控制器,并在其中一项操作上添加[Authorize]。 还修改Home View文件以显示用户的声明以及cookie属性:

@using Microsoft.AspNetCore.Authentication

<h2>Claims</h2>

<dl>
    @foreach (var claim in User.Claims)
    {
        <dt>@claim.Type</dt>
        <dd>@claim.Value</dd>
    }
</dl>

<h2>Properties</h2>

<dl>
    @foreach (var prop in (await Context.AuthenticateAsync()).Properties.Items)
    {
        <dt>@prop.Key</dt>
        <dd>@prop.Value</dd>
    }
</dl>

如果现在使用浏览器导航到该控制器,将尝试重定向到IdentityServer-这将导致错误,因为尚未注册MVC客户端。

添加对OpenID Connect Identity Scope的支持

与OAuth 2.0类似,OpenID Connect也使用范围概念。 同样,范围代表您要保护的内容以及客户端要访问的内容。 与OAuth相比,OIDC中的范围不代表API,而是身份数据,例如用户ID,名称或电子邮件地址。

通过修改Config.cs中的GetIdentityResources方法,添加对标准openid(主题id)和profile(名字,姓氏等)范围的支持:

public static IEnumerable<IdentityResource> GetIdentityResources()
{
    return new List<IdentityResource>
    {
        new IdentityResources.OpenId(),
        new IdentityResources.Profile(),
    };
}
注意

所有标准范围及其相应的声明均可在OpenID Connect规范中找到

为OpenID Connect隐式流添加客户端

最后一步是将MVC客户端的新配置条目添加到IdentityServer。

基于OpenID Connect的客户端与到目前为止我们添加的OAuth 2.0客户端非常相似。 但是由于OIDC中的流始终是交互的,因此我们需要向配置中添加一些重定向URL。

将以下内容添加到您的客户端配置中:

public static IEnumerable<Client> GetClients()
{
    return new List<Client>
    {
        // 其他代码省略...

        // OpenID Connect隐式流MVC客户端
        new Client
        {
            ClientId = "mvc",
            ClientName = "MVC Client",
            AllowedGrantTypes = GrantTypes.Implicit,

            // 登录后重定向到的位置
            RedirectUris = { "http://localhost:5002/signin-oidc" },

            // 注销后重定向到的位置
            PostLogoutRedirectUris = { "http://localhost:5002/signout-callback-oidc" },

            AllowedScopes = new List<string>
            {
                IdentityServerConstants.StandardScopes.OpenId,
                IdentityServerConstants.StandardScopes.Profile
            }
        }
    };
}

测试客户端

现在终于可以为新的MVC客户端准备好一切了。

通过导航到受保护的控制器操作来触发身份验证握手。 您应该看到重定向到IdentityServer上的登录页面。

image.png

成功登录后,将向用户显示同意屏幕。 用户可以在此处决定是否要将其身份信息发布到客户端应用程序。

注意

可以使用客户端配置上的RequireConsent属性在每个客户端上关闭同意。

image.png

之后,IdentityServer将重定向回MVC客户端,在该客户端上,OpenID Connect身份验证处理程序将处理响应并通过设置cookie在本地登录用户。 最后,MVC视图将显示cookie的内容。

如您所见,cookie包含两部分,用户声明和一些元数据。 此元数据还包含由IdentityServer发行的原始令牌。 可以将此令牌复制到jwt.io来检查其内容。

添加登出

最后一步是将注销添加到MVC客户端。

使用IdentityServer之类的身份验证服务,仅清除本地应用程序cookie是不够的。 另外,您还需要往返IdentityServer以清除中央单点登录会话。

确切的协议步骤是在OpenID Connect处理程序内部实现的,只需将以下代码添加到某个控制器即可触发注销:

public IActionResult Logout()
{
    return SignOut("Cookies", "oidc");
}

这将清除本地cookie,然后重定向到IdentityServer。 IdentityServer将清除其cookie,然后为用户提供一个链接以返回到MVC应用程序。

进一步的实验

如上所述,默认情况下,OpenID Connect处理程序要求配置文件范围。 此范围还包括名称或网站之类的声明。

让我们向用户添加这些声明,以便IdentityServer可以将它们放入身份令牌中:

public static List<TestUser> GetUsers()
{
    return new List<TestUser>
    {
        new TestUser
        {
            SubjectId = "1",
            Username = "alice",
            Password = "password",

            Claims = new []
            {
                new Claim("name", "Alice"),
                new Claim("website", "https://alice.com")
            }
        },
        new TestUser
        {
            SubjectId = "2",
            Username = "bob",
            Password = "password",

            Claims = new []
            {
                new Claim("name", "Bob"),
                new Claim("website", "https://bob.com")
            }
        }
    };
}

下次您进行身份验证时,您的声明页面现在将显示其他声明。

随时添加更多声明-以及更多范围。 您可以在OpenID Connect中间件上的Scope属性中配置在身份验证期间将哪些范围发送到IdentityServer。

还值得注意的是,令牌声明的检索是可扩展性点-IProfileService。 由于我们使用的是AddTestUsers,因此默认情况下使用TestUserProfileService。 您可以在此处检查源代码以查看其工作方式。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,907评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,987评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,298评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,586评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,633评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,488评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,275评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,176评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,619评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,819评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,932评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,655评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,265评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,871评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,994评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,095评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,884评论 2 354

推荐阅读更多精彩内容