使用OpenID Connect添加用户认证
2017-11-08

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 认证

在类Startup的 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.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 了,直接用的官方的。

.NET Core 交流群:4656606
原文地址: http://www.cnblogs.com/stulzq/p/7797341.html