ASP.NET Core的身份认证框架IdentityServer4(9)-使用OpenID Connect添加用户认证

OpenID Connect

OpenID Connect 1.0是OAuth 2.0协议之上的一个简单的身份层。 它允许客户端基于授权服务器执行的身份验证来验证最终用户的身份,以及以可互操作和类似REST的方式获取关于最终用户的基本配置文件信息。

OpenID Connect允许所有类型的客户端(包括基于Web的移动和JavaScript客户端)请求和接收关于认证会话和最终用户的信息。 规范套件是可扩展的,允许参与者使用可选功能,例如身份数据的加密,OpenID提供商的发现和会话管理。

OpenID Connect 和 OAuth的关系

OpenID Connect 在OAuth2上构建了一个身份层,是一个基于OAuth2协议的身份认证标准协议。我们都知道OAuth2是一个授权协议,它无法提供完善的身份认证功能,OpenID Connect 使用OAuth2的授权服务器来为第三方客户端提供用户的身份认证,并把对应的身份认证信息传递给客户端,且可以适用于各种类型的客户端(比如服务端应用,移动APP,JS应用),且完全兼容OAuth2,也就是说你搭建了一个OpenID Connect 的服务后,也可以当作一个OAuth2的服务来用。应用场景如图 (摘自:http://www.cnblogs.com/linianhui/archive/2017/05/30/openid-connect-core.html

简而言之,OpenID Connect是在OAuth2.0上进行的一个扩展协议,OpenID Connect=Identity +OAuth 2.0,OpenID Connect主要用于身份认证(Authentication),OAuth主要用于授权(Authorization)。

更深入了解OpenID Connect和OAuth,如果你准备使用IdentityServer4,十分推荐阅读这几篇文章:http://www.cnblogs.com/linianhui/category/929878.html

创建一个MVC客户端

1.新建一个ASP.NET Core MVC应用程序

2.配置 OpenID Connect 认证

在类StartupConfigureServices方法中添加以下代码:

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.SignInScheme = "Cookies";

        options.Authority = "http://localhost:5000";
        options.RequireHttpsMetadata = false;

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

AddAuthentication将认证服务添加到依赖注入容器中,使用Cookie作为验证用户的主要方法(通过"Cookies"作为 DefaultScheme)。

DefaultChallengeScheme 设置为"oidc"(OIDC是OpenID Connect的简称),因为当我们需要用户登录时,我们将使用OpenID Connect方案。

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

最后,AddOpenIdConnect用于配置执行OpenID Connect协议的处理程序。Authority表示id4服务的地址。 然后我们通过ClientId识别该客户端。SignInScheme 用于在OpenID Connect协议完成后使用cookie处理程序发出cookie。 而SaveTokens用于在Cookie中保存IdentityServer中的令牌(稍后将需要)。

然后确保在每个请求上执行认证服务,在Startup中的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之前添加。

最后一步是触发认证。为了进入HomeController,并在其中一个Action上添加特性[Authorize]

还要修改该Action的View以显示用户的信息,例如:

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

如果您现在使用浏览器访问HomeController,将会导致重定向到IdentityServer,这将导致错误,因为MVC客户端尚未注册。

添加OpenID Connect Identity Scopes的支持

与OAuth 2.0类似,OpenID Connect也使用Scopes概念。 再次,Scopes代表您想要保护的客户端希望访问的内容。 与OAuth相反,OIDC中的范围不代表API,而是代表用户ID,姓名或电子邮件地址等身份信息。

Config.cs中添加如下代码:

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

所有标准Scopes及其相应的声明都可以在OpenID Connect规范中找到。

然后,您需要将这些身份资源添加到Startup.cs中的IdentityServer配置中。使用AddInMemoryIdentityResources扩展方法调用AddIdentityServer()

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

    // configure identity server with in-memory stores, keys, clients and scopes
    services.AddIdentityServer()
        .AddDeveloperSigningCredential()
        .AddInMemoryIdentityResources(Config.GetIdentityResources())
        .AddInMemoryApiResources(Config.GetApiResources())
        .AddInMemoryClients(Config.GetClients())
        .AddTestUsers(Config.GetUsers());
}

为OpenID Connect implicit flow 添加客户端

Implicit Flow指的是使用OAuth2的Implicit流程获取Id Token和Access Token

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

基于OpenID Connect的客户端与我们迄今添加的OAuth 2.0客户端非常相似。 但是由于OIDC中的流程始终是交互式的,我们需要在配置中添加一些重定向URL。

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

public static IEnumerable<Client> GetClients()
{
    return new List<Client>
    {
        // other clients omitted...

        // OpenID Connect implicit flow client (MVC)
        new Client
        {
            ClientId = "mvc",
            ClientName = "MVC Client",
            AllowedGrantTypes = GrantTypes.Implicit,

            // where to redirect to after login
            RedirectUris = { "http://localhost:5002/signin-oidc" },

            // where to redirect to after logout
            PostLogoutRedirectUris = { "http://localhost:5002/signout-callback-oidc" },

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

测试客户端

通过访问受保护的Action来触发身份验证握手。 你应该看到重定向到IdentityServer的登录页面。

成功登录后,用户将看到同意画面。 在这里,用户可以决定是否要将他的身份信息发布到客户端应用程序。

可以使用客户端对象上的RequireConsent属性以每个客户端为基础关闭同意询问。

最后浏览器重定向到客户端应用程序,该应用程序显示了用户的声明。

在开发过程中,您有时可能会看到一个异常,说明令牌无法验证。 这是因为签名密钥信息是即时创建的,并且只保存在内存中。 当客户端和IdentityServer不同步时,会发生此异常。 只需在客户端重复操作,下次元数据已经追上,一切都应该正常工作。

添加注销

最后一步是给MVC客户端添加注销功能。

使用IdentityServer等身份验证服务,仅清除本地应用程序Cookie是不够的。 此外,您还需要往身份服务器交互,以清除单点登录会话。

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

public async Task Logout()
{
    await HttpContext.SignOutAsync("Cookies");
    await HttpContext.SignOutAsync("oidc");
}

这将清除本地cookie,然后重定向到IdentityServer。 IdentityServer将清除它的cookie,然后给用户一个链接返回到MVC应用程序。

进一步实验

如上所述,OpenID Connect中间件默认要求配置 profile scope。 这个scope还包括像名字或网站这样的声明。

让我们将这些声明添加到用户,以便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属性是您配置哪些Scope将在身份验证期间发送到IdentityServer。

值得注意的是,对令牌中身份信息的遍历是一个扩展点 - IProfileService。因为我们正在使用 AddTestUser,所以默认使用的是 TestUserProfileService。你可以检出这里的源代码来查看它的工作原理。

地址

Demo下载地址:https://github.com/IdentityServer/IdentityServer4.Samples/tree/release/Quickstarts/3_ImplicitFlowAuthentication

参考官方文档:https://identityserver4.readthedocs.io/en/release/quickstarts/3_interactive_login.html

官方的Demo已经更新到最新的.NET Core 2.0 所以我不在单独写Demo 了,直接用的官方的。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏云计算教程系列

如何在CentOS 7上使用Docker安装Prometheus

Prometheus是一个开源监控系统和时间序列数据库。它涉及监控的许多方面,例如度量标准的生成和收集,在仪表板上绘制结果数据以及针对异常情况发出警报。为实现这...

1.1K0
来自专栏Linux 杂货铺

使用Kubernetes管理Docker集群

Kubernetes是一个来管理容器化应用程序的开源平台。如果您使用Docker将应用部署到多个服务器节点上,Kubernetes集群就可以管理您的服务器和应用...

3K10
来自专栏云计算教程系列

使用GitLab构建Docker镜像并托管

容器化正迅速成为在云环境中打包和部署应用程序的最常用方法。它提供的标准化,以及其资源效率和灵活性,使其成为现代DevOps思维模式的重要推动者。当您的应用程序和...

1.5K0
来自专栏有困难要上,没有困难创造困难也要上!

使用pipework将Docker容器配置到本地网络环境中

45212
来自专栏JMCui

Docker 系列六(Docker Swarm 项目).

    随着互联网快速发展,以及微服务架构的流行,服务器的压力越来越大。上一篇介绍的 Docker Compose 项目,可以将多个容器捏合在一起,实现容器间的...

944
来自专栏宝哥的专栏

Docker系列学习文章 - 存储基本配置(八)

| 导语 通过前面的文章学会后,我们运行一个容器,打包制作一个镜像没啥问题了。但是要真正在生产上运用docker,我们还差两招很重要的。一个是数据卷的配置,还有...

1.6K27
来自专栏前端开发

sublime注册码失效解决办法

该窗口的大致含义就是说你使用的 License 是一个 Sublime Text 2 版本的 Licence(当前是3),问你是否需要升级你的 Licence,...

2304
来自专栏云计算教程系列

使用GitLab构建Docker镜像并托管

容器化正迅速成为在云环境中打包和部署应用程序的最常用方法。它提供的标准化,以及其资源效率和灵活性,使其成为现代DevOps思维模式的重要推动者。当您的应用程序和...

4012
来自专栏性能与架构

Docker容器案例:应用 Mysql

前阶段体验 Mysql 的新版本 5.7.13,由于机器里已经有 Mysql了,再安装另一个版本会有一些麻烦,为了简单,便使用 Docker 容器来安装 可能有...

3453
来自专栏小狼的世界

谈一下Docker与Kubernetes集群的日志和日志管理

日志对于我们管理Kubernetes集群及其上的应用具有非常重要的作用,特别是在出现故障或者Bug的时候。如果你能回答下面几个问题,那么可以不用再看本文了,如果...

2951

扫码关注云+社区

领取腾讯云代金券