ASP.NET core 系列 56 IS4 使用 OpenID Connect 添加用户认证 一. 概述
在前二篇中讲到了客户端授权的二种方式: GrantTypes.ClientCredentials 凭据授权和 GrantTypes.ResourceOwnerPassword 密码授权, 都是 OAuth2.0 协议. 本篇使用 OpenID Connect 添加用户认证, 客户端授权是 GrantTypes.Implicit 隐式流授权, 是 OCID 协议. 本篇示例中只有二个项目: 一个 IdentityServer 的 mvc 应用程序, 一个客户端 mvc 应用程序 (用户 client 端).
下面介绍身份认证交互流程:
(1) 先启动 IdentityServer 程序 https://localhost:5000
(2) 启动客户端 MvcClient 程序 https://localhost:5002
(3) Client 用户访问 https://localhost:5002/Secure 时, 想获取个人信息和资料信息, 如果用户没有进行身份认证, OIDC 会重定向
(4) 重定向到 IdentityServer 服务端站点的登录页: https://localhost:5000/Account/Login?ReturnUrl=xxx
(5) 用户登录成功后. 自动跳回到 MvcClient 客户端站点, 访问地址 https://localhost:5002/Home/Secure. 获取了当前个人信息和资料信息
上面的步骤了解到: Client 用户要访问个人信息时, 必须先进行, 交互式用户身份验证 Account/Login, 验证通过后, 客户端浏览器会保存服务令牌在 cookie 中. 需要注意的是: 在隐式授权中, 令牌是通过浏览器传输, 在 MvcClient 客户端程序中用 HttpClient 获取 cookie 中的令牌来请求 API, 返回是 http 401 状态, 这是因为该令牌是身份令牌还非访问令牌.
从 GitHub 中下载开源项目, 可以快速入门启动 OpenID Connect 协议的交互式用户身份验证支持. 在实际项目中, 也可以将示例中的控制器, 视图, 模型和 CSS 整合到自己项目的 IdentityServer web 应用程序中.
二. IdentityServer MVC 应用程序
因为是交互式用户身份验证, 必须有 UI 界面, 所以 IdentityServer 是一个 MVC 应用程序. 下面是示例项目目录:
Account: 客户端站点重定向到服务端站点, 用于用户登录或注销.
Grants: 用于撤销客户端访问权限.
Consent : 是用户登录成功后, 跳转到授权许可的 UI 界面. 用户可以决定是否要将他的身份信息发布到客户端应用程序.
Device : 是设备流交互服务.
Diagnostics: 是诊断查看个人身份认证 cookie 信息.
1.1 定义客户端
在 config.cs 类中, 定义客户端, 将 OpenID Connect 隐式流添加到客户端. 基于 OpenID Connect 的客户端与 OAuth 2.0 客户端非常相似. 但由于 OIDC 中的流程始终是交互式的, 因此我们需要在配置中添加一些重定向 URL.
- public static IEnumerable GetClients()
- {
- return new List
- {
- new Client
- {
- ClientId = "client",
- // no interactive user, use the clientid/secret for authentication
- AllowedGrantTypes = GrantTypes.ClientCredentials,
- // secret for authentication
- ClientSecrets =
- {
- new Secret("secret".Sha256())
- },
- // scopes that client has access to
- AllowedScopes = { "api1" }
- },
- // resource owner password grant client
- new Client
- {
- ClientId = "ro.client",
- AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
- ClientSecrets =
- {
- new Secret("secret".Sha256())
- },
- AllowedScopes = { "api1" }
- },
- // OpenID Connect implicit flow client (MVC)
- new Client
- {
- ClientId = "mvc",
- ClientName = "MVC Client",
- AllowedGrantTypes = GrantTypes.Implicit,
- //OIDC 中的流程始终是交互式的
- // 登录后要重定向到哪里
- RedirectUris = { "https://localhost:5002/signin-oidc" },
- // 注销后重定向到哪里
- PostLogoutRedirectUris = { "https://localhost:5002/signout-callback-oidc" },
- // 与 OAuth 2.0 类似, OpenID Connect 也使用范围概念, 与 OAuth 相比, OIDC 中的范围不代表 API, 而是代表用户 ID, 名称或电子邮件地址等身份数据.
- AllowedScopes = new List
- {
- // 主题 id, 也是用户唯一 ID(最低要求)
- IdentityServerConstants.StandardScopes.OpenId,
- // 个人信息的 claims, 名称或电子邮件地址等身份数据
- IdentityServerConstants.StandardScopes.Profile
- }
- }
- };
- }
1.2 定义 OIDC 范围
- public static IEnumerable GetIdentityResources()
- {
- return new List
- {
- new IdentityResources.OpenId(),
- new IdentityResources.Profile(),
- };
- }
1.3 在 Startup 启动类中启动 IdentityServer 服务
- var builder = services.AddIdentityServer()
- .AddInMemoryIdentityResources(Config.GetIdentityResources())
- .AddInMemoryApiResources(Config.GetApis())
- .AddInMemoryClients(Config.GetClients())
- .AddTestUsers(Config.GetUsers());
二. MvcClient 客户端应用程序
2.1 Startup 启动类
添加对 OpenID Connect 身份验证的支持, 在启动时将以下代码添加到 ConfigureServices 方法中:
- public void ConfigureServices(IServiceCollection services)
- {
- services.AddMvc();
- // 关闭了 JWT 声明类型映射
- JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
- // 添加 authentication 到服务集合中
- services.AddAuthentication(options =>
- {
- // 使用 cookie 本地登录用户
- options.DefaultScheme = "Cookies";
- // 用户登录时, 使用 OpenID 连接协议.
- options.DefaultChallengeScheme = "oidc";
- })
- // 添加对 cookie 的处理支持
- .AddCookie("Cookies")
- //oidc 处理程序
- .AddOpenIdConnect("oidc", options =>
- {
- // 受信任的 IdentityServer 服务地址
- options.Authority = "https://localhost:5000";
- options.RequireHttpsMetadata = false;
- // 客户端标识
- options.ClientId = "mvc";
- // 将 IdentityServer 中的令牌持久化到 cookie 中 (客户端浏览器中)
- options.SaveTokens = true;
- });
- }
- public void Configure(IApplicationBuilder App, IHostingEnvironment env)
- {
- if (env.IsDevelopment())
- {
- App.UseDeveloperExceptionPage();
- }
- else
- {
- App.UseExceptionHandler("/Home/Error");
- }
- // 每个请求都能执行身份验证服务
- App.UseAuthentication();
- App.UseStaticFiles();
- App.UseMvcWithDefaultRoute();
- }
2.2 访问个人信息
由于使用的是 OpenID Connect, 是基于浏览器的交互式身份认证. 在 action 中添加一个 [Authorize], 会触发身份验证握手. 下面 Secure 方法, 显示当前用户的声明以及 cookie 属性. 握手时重定向到 IdentityServer 服务站点下进行登录.
- // 身份验证握手, 采用 oidc, 重定向到 IdentityServer 进行登录
- [Authorize]
- public IActionResult Secure()
- {
- ViewData["Message"] = "Secure page.";
- return View();
- }
下面是 Secure 视图:
- @using Microsoft.AspNetCore.Authentication
- Claims
- @foreach (var claim in User.Claims)
- {
- @claim.Type
- @claim.Value
- }
- Properties
- @foreach (var prop in (await Context.AuthenticateAsync()).Properties.Items)
- {
- @prop.Key
- @prop.Value
- }
2.3 注销
使用 IdentityServer 等身份验证服务, 仅清除本地应用程序 cookie 是不够的 (客户端浏览器). 此外, 还需要向 IdentityServer 进行往返以清除中央单点登录会话.
- public IActionResult Logout()
- {
- return SignOut("Cookies", "oidc");
- }
触发 Logout 后, 会清除本地 cookie(客户端浏览器), 然后重定向到 IdentityServer.IdentityServer 将清除其 cookie(服务端浏览器), 然后为用户提供返回 MVC 应用程序的链接.
参考文献
使用 OpenID Connect 添加用户认证
来源: https://www.2cto.com/kf/201904/804506.html