OpenID Connect 1.0是OAuth 2.0协议之上的一个简单的身份层。 它允许客户端基于授权服务器执行的身份验证来验证最终用户的身份,以及以可互操作和类似REST的方式获取关于最终用户的基本配置文件信息。 OpenID Connect允许所有类型的客户端(包括基于Web的移动和JavaScript客户端)请求和接收关于认证会话和最终用户的信息。 规范套件是可扩展的,允许参与者使用可选功能,例如身份数据的加密,OpenID提供商的发现和会话管理
OpenID Connect 在OAuth2上构建了一个身份层,是一个基于OAuth2协议的身份认证标准协议。我们都知道OAuth2是一个授权协议,它无法提供完善的身份认证功能,OpenID Connect 使用OAuth2的授权服务器来为第三方客户端提供用户的身份认证,并把对应的身份认证信息传递给客户端,且可以适用于各种类型的客户端(比如服务端应用,移动APP,JS应用,winform应用),且完全兼容OAuth2,也就是说你搭建了一个OpenID Connect 的服务后,也可以当作一个OAuth2的服务来用。
IdentityServer 内置了 OpenID Connect 需要的所有协议支持。你需要提供必需的 UI 部分,包括 登录、注销、授权确认以及错误页。
因为在每个 IdentityServer 的实现中,视觉、感觉以及实际工作流可能总是有所不同的,所以我们提供了一套基于 MVC 的样例 UI,你可以将其作为启动点来使用。
这套 UI 可以在 快速入门仓库 找到。你还可以克隆或下载这个仓库,将其中的控制器、视图、模型以及 CSS 放到你的 Web 应用程序中。
你还可以在你的 Web 应用程序中以命令行的方式运行以下命令来自动下载:
iex ((New-Object System.Net.WebClient).DownloadString('https://raw.githubusercontent.com/IdentityServer/IdentityServer4.Quickstart.UI/release/get.ps1'))
查看 自述文件 以了解更多 快速入门UI 相关的信息。
注意: UI 仓库的 release 分支拥有与最新发布的稳定版相匹配的 UI。dev 分支则与 IdentityServer4 的当前开发构建相符。如果你想要寻找指定版本的 UI,请查看相应的标签。
花一些时间去查阅控制器和模型,你越是了解他们,将来要修改他们就越简单。大部分代码都以“功能目录”的样式放在 “Quickstart” 文件夹下,如果这种样式不适合你,那就按照你想要的方式随意组织代码。
接下来你将向解决方案添加一个 MVC 应用程序,可以使用 ASP.NET Core "Web 应用程序" 模板来实现。 将应用程序配置为使用 5002 端口(可以查看概览部分以了解如何配置)。
为了能向 MVC 应用程序添加 OpenID Connect 认证支持,请添加如下 NuGet 程序包:
然后添加这两个中间件到你的管道中 —— Cookies 对应的中间件很简单:
app.UseCookieAuthentication(new CookieAuthenticationOptions { AuthenticationScheme="Cookies" });
OpenID Connect 中间件需要稍微多一些配置。将它指向 Identity Server,指定一个客户端 ID 并且告诉它哪个中间件将会负责本地登陆(也就是 cookies 中间件)。此外,我们关闭了 JWT 身份信息类型映射,这样就允许 well-known 身份信息(比如,“sub” 和 “idp”) 无干扰地流过。这个身份信息类型映射的 “清理” 必须在调用 UseOpenIdConnectAuthentication() 之前完成:
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions { AuthenticationScheme = "oidc", SignInScheme = "Cookies", Authority = "http://localhost:5000", RequireHttpsMetadata = false, ClientId = "mvc", SaveTokens = true });
两个中间件都应该在 MVC 之前添加到管道。
下一个步骤是触发认证握手,为此打开 home 控制器并添加 [Authorize] 到其中一个 action 上。另外,修改 action 对应的视图以显示用户的身份信息,比如:
<dl>
@foreach (var claim in User.Claims)
{
<dt>@claim.Type</dt>
<dd>@claim.Value</dd>
}
</dl>
现在,如果你使用浏览器导航到上述控制器,一个Mvc客户端将视图重定向到 IdentityServer - 这会导致错误,因为 MVC 客户端还没有注册(在 IdentityServer 上定义)呢。
与 OAuth 2.0 相似,OpenID Connect 也使用 scopes 这个概念。再一次说明,Scopes 表示你想要保护的和客户端想要访问的事物。在与 OAuth 相比,OIDC(OpenID Connect) 中的 scopes 不仅代表 API,还代表了诸如 用户id、用户名 或 邮箱地址等身份数据。
通过(在 Config.cs 中)添加新的帮助器来创建 IdentityResource 对象的集合,可以添加对标准 openid(subject id,这里指的是用户id)和 profile(姓氏,名称 等等)等 scopes 的支持:
public static IEnumerableGetIdentityResources() { return new List { new IdentityResources.OpenId(), new IdentityResources.Profile(), }; }
注意:所有标准的 Scopes 和他们对应的 身份信息 都可以在 OpenID Connect 规范 中找到。
接下来在 Startup.cs 中你要将这些身份资源添加到你的 IdentityServer 配置。在你调用 AddIdentityServer() 的地方使用 AddInMemoryIdentityResources 扩展方法即可:
public void ConfigureServices(IServiceCollection services) { // 使用内存存储,密钥,客户端和资源来配置身份服务器。 services.AddIdentityServer() .AddTemporarySigningCredential() .AddInMemoryApiResources(Config.GetApiResources()) .AddInMemoryClients(Config.GetClients()) .AddTestUsers(Config.GetUsers()) .AddInMemoryIdentityResources(Config.GetIdentityResources()); }
最后一个步骤是为 IdentityServer 添加一个新的客户端。
目前,我们添加的基于 OIDC 的客户端与 OAuth 2.0 客户端非常相似。但是由于 OIDC 中的流总是交互式的,所以我们需要添加一些重定向 URL 到我们的配置中。
添加以下代码到你的客户端配置中:
public static IEnumerableGetClients() { return new List { // 省略的客户端... // OIDC 隐式流客户端(MVC) new Client { ClientId = "mvc", ClientName = "Mvc 客户端", AllowedGrantTypes = GrantTypes.Implicit, // 登录后重定向到的地址 RedirectUris = { "http://localhost:5002/signin-oidc" }, // 注销后重定向到的地址 AllowedScopes = new List { IdentityServerConstants.StandardScopes.OpenId, IdentityServerConstants.StandardScopes.Profile } } }; }
现在,对于新的 MVC 客户端,一切都准备好了。
将浏览器导航到受保护的 控制器 action 以触发认证握手。你应该能看到客户端将你重定向到了 IdentityServer 的登录页。
登录成功后,用户将在授权确认页中被呈现出来。在这里用户可以决定他是否想要发布他的身份信息给客户端应用程序。
注意: 授权确认页可以通过客户端定义对象的 RequireConsent 属性被关闭(以每个客户端为单位)。
最终浏览器将被重定向回客户端应用程序,即展示用户的身份信息。
注意:在开发期间你有时候可能会看到 无法验证令牌 的异常信息。这是因为实际上签名密钥材料是凭空产生的,并且在只内存中驻留。这个异常在 客户端 与 IdentityServer 不同步时就会发生。简单地重复客户端上的操作,等到下一次元数据被捕获时,一切都会再次正常工作的。
最后的最后,是给 MVC 客户端添加 注销功能。
通过 IdentityServer 这样的服务进行身份认证,单单清除本地应用程序的 Cookies 是不够的。你还需要往返一次 IdentityServer 来清理集中式单点登录会话。
具体的协议步骤都在 OpenID Connect 中间件中实现了,简单地添加以下代码到某个控制器中就可以用来触发注销:
public async Task Logout() { await HttpContext.Authentication.SignOutAsync("Cookies"); await HttpContext.Authentication.SignOutAsync("oidc");//注销基于iodc的身份认证,这里在如果是针对id3的注销将可能出现http500的错误,具体解决方案将会在本网站以后的文章公布 }
如前面所说,OpenID Connect 中间件默认会请求 profile scope。这个 scope 还包含了用户名或个人主页等身份信息。
让我们将这些身份信息添加到用户定义里面,这样的话 IdentityServer 就可以把它们放到身份令牌中了:
public static ListGetUsers() { return new List () { new TestUser { SubjectId="1", Username="胡德海", Password="password", Claims = new [] { new Claim("name", "胡德海"), new Claim("website", "https://alice.com") } }, new TestUser { SubjectId="2", Username="刁树棍", Password="password", Claims = new [] { new Claim("name", "刁树棍"), new Claim("website", "https://bob.com") } } }; }
下一次你认证的时候,你的身份信息页将会显示额外的身份信息。
请随意添加更多的身份信息 - 还有更多的 scopes。OpenID Connect 中间件上的 Scope 属性是你用来配置哪些 Scopes 将在认证期间被发送到 IdentityServer 的地方。
值得注意的是,对令牌中身份信息的遍历是一个扩展点 - IProfileService。因为我们正在使用 AddTestUser,所以默认使用的是 TestUserProfileService。你可以检出这里的源代码来查看它的工作原理。