Identity Server 4使用OpenID Connect添加用户身份验证(三)

一、说明

基于上一篇文章中的代码进行继续延伸,只需要小小的改动即可,不明白的地方可以先看看本人上一篇文章及源码  Identity Server 4资源拥有者密码认证控制访问API(二)

 GitHub项目源码:https://github.com/li215704087/IdentityServer4

二、添加UI

官方GitHub:https://github.com/IdentityServer/IdentityServer4.Quickstart.UI

 

OpenID Connect 所需的所有协议支持都已内置到 IdentityServer 中。您需要为提供必要的UI部件:登录,注销,同意授权和错误页面。

根据业务场景的不同对 IdentityServer 的实现也有所不同,但我们提供了一个基于 MVC 的示例UI,您可以将其用作起步。

可以在快速入门UI仓库中找到此UI。 您可以克隆或下载此repo,并将Controller,View,Model和CSS放入IdentityServer Web 应用程序中。

或者,您可以使用.NET CLI(从 QuickStartIdentityServer4 文件夹中运行命令):

dotnet new -i identityserver4.templates dotnet new is4ui

添加 MVC UI 后,您还需要在 DI 系统和管道中启用 MVC。 当您查看Startup.cs时,您将在 ConfigureServices 和 Configure 方法中找到有关如何启用MVC的注释

三、运行QuickStartIdentityServer4项目

Identity Server 4使用OpenID Connect添加用户身份验证(三)

 四、环境配置

一、QuickStartIdentityServer4项目中Config文件增加配置

 

// Clients集合中增加 基于OIDC客户端配置 new Client { ClientId="sample_mvc_client", ClientName="Sample MVC Client", ClientSecrets= { new Secret("sample_client_secret".Sha256()) }, AllowedGrantTypes=GrantTypes.Code, RedirectUris={ "http://localhost:4001/signin-oidc"}, // 登录成功之后的回调地址 PostLogoutRedirectUris={ "http://localhost:4001/signout-callback-oidc" }, // 注销/登出之后的回调地址 AllowedScopes={ IdentityServerConstants.StandardScopes.OpenId, IdentityServerConstants.StandardScopes.Profile, "sample_api" // 用于oidc认证成功之后访问项目API的范围api接口 }, RequireConsent=true // 是否需要用户同步,当用户登录的时候需要用户进行是否同意 }

// 基于OIDC协议         public static IEnumerable<IdentityResource> IdentityResources => new List<IdentityResource>         {            new IdentityResources.OpenId(),           new IdentityResources.Profile()         };          // 基于OIDC添加测试用户         public static List<TestUser> Users => new List<TestUser>() {                     new TestUser()           {               SubjectId="1",               Username="admin",               Password="123456777"           }         };

二、新增web项目Sample.MvcClient ,端口号4001   NuGet:Microsoft.AspNetCore.Authentication.OpenIdConnect

1、增加SameSiteCookiesServiceCollectionExtensions.cs扩展类,该类主要是为了解决认证成功,页面跳转异常问题

public static class SameSiteCookiesServiceCollectionExtensions     {         /// <summary>         /// -1 defines the unspecified value, which tells ASPNET Core to NOT         /// send the SameSite attribute. With ASPNET Core 3.1 the         /// <seealso cref="SameSiteMode" /> enum will have a definition for         /// Unspecified.         /// </summary>         private const SameSiteMode Unspecified = (SameSiteMode)(-1);          /// <summary>         /// Configures a cookie policy to properly set the SameSite attribute         /// for Browsers that handle unknown values as Strict. Ensure that you         /// add the <seealso cref="Microsoft.AspNetCore.CookiePolicy.CookiePolicyMiddleware" />         /// into the pipeline before sending any cookies!         /// </summary>         /// <remarks>         /// Minimum ASPNET Core Version required for this code:         ///   - 2.1.14         ///   - 2.2.8         ///   - 3.0.1         ///   - 3.1.0-preview1         /// Starting with version 80 of Chrome (to be released in February 2020)         /// cookies with NO SameSite attribute are treated as SameSite=Lax.         /// In order to always get the cookies send they need to be set to         /// SameSite=None. But since the current standard only defines Lax and         /// Strict as valid values there are some browsers that treat invalid         /// values as SameSite=Strict. We therefore need to check the browser         /// and either send SameSite=None or prevent the sending of SameSite=None.         /// Relevant links:         /// - https://tools.ietf.org/html/draft-west-first-party-cookies-07#section-4.1         /// - https://tools.ietf.org/html/draft-west-cookie-incrementalism-00         /// - https://www.chromium.org/updates/same-site         /// - https://devblogs.microsoft.com/aspnet/upcoming-samesite-cookie-changes-in-asp-net-and-asp-net-core/         /// - https://bugs.webkit.org/show_bug.cgi?id=198181         /// </remarks>         /// <param name="services">The service collection to register <see cref="CookiePolicyOptions" /> into.</param>         /// <returns>The modified <see cref="IServiceCollection" />.</returns>         public static IServiceCollection ConfigureNonBreakingSameSiteCookies(this IServiceCollection services)         {             services.Configure<CookiePolicyOptions>(options =>             {                 options.MinimumSameSitePolicy = Unspecified;                 options.OnAppendCookie = cookieContext =>                    CheckSameSite(cookieContext.Context, cookieContext.CookieOptions);                 options.OnDeleteCookie = cookieContext =>                    CheckSameSite(cookieContext.Context, cookieContext.CookieOptions);             });              return services;         }          private static void CheckSameSite(HttpContext httpContext, CookieOptions options)         {             if (options.SameSite == SameSiteMode.None)             {                 var userAgent = httpContext.Request.Headers["User-Agent"].ToString();                  if (DisallowsSameSiteNone(userAgent))                 {                     options.SameSite = Unspecified;                 }                 else                 {                     options.SameSite = SameSiteMode.Lax;  // 增加这句                 }             }         }          /// <summary>         /// Checks if the UserAgent is known to interpret an unknown value as Strict.         /// For those the <see cref="CookieOptions.SameSite" /> property should be         /// set to <see cref="Unspecified" />.         /// </summary>         /// <remarks>         /// This code is taken from Microsoft:         /// https://devblogs.microsoft.com/aspnet/upcoming-samesite-cookie-changes-in-asp-net-and-asp-net-core/         /// </remarks>         /// <param name="userAgent">The user agent string to check.</param>         /// <returns>Whether the specified user agent (browser) accepts SameSite=None or not.</returns>         private static bool DisallowsSameSiteNone(string userAgent)         {             // Cover all iOS based browsers here. This includes:             //   - Safari on iOS 12 for iPhone, iPod Touch, iPad             //   - WkWebview on iOS 12 for iPhone, iPod Touch, iPad             //   - Chrome on iOS 12 for iPhone, iPod Touch, iPad             // All of which are broken by SameSite=None, because they use the             // iOS networking stack.             // Notes from Thinktecture:             // Regarding https://caniuse.com/#search=samesite iOS versions lower             // than 12 are not supporting SameSite at all. Starting with version 13             // unknown values are NOT treated as strict anymore. Therefore we only             // need to check version 12.             if (userAgent.Contains("CPU iPhone OS 12")                || userAgent.Contains("iPad; CPU OS 12"))             {                 return true;             }              // Cover Mac OS X based browsers that use the Mac OS networking stack.             // This includes:             //   - Safari on Mac OS X.             // This does not include:             //   - Chrome on Mac OS X             // because they do not use the Mac OS networking stack.             // Notes from Thinktecture:              // Regarding https://caniuse.com/#search=samesite MacOS X versions lower             // than 10.14 are not supporting SameSite at all. Starting with version             // 10.15 unknown values are NOT treated as strict anymore. Therefore we             // only need to check version 10.14.             if (userAgent.Contains("Safari")                && userAgent.Contains("Macintosh; Intel Mac OS X 10_14")                && userAgent.Contains("Version/"))             {                 return true;             }              // Cover Chrome 50-69, because some versions are broken by SameSite=None             // and none in this range require it.             // Note: this covers some pre-Chromium Edge versions,             // but pre-Chromium Edge does not require SameSite=None.             // Notes from Thinktecture:             // We can not validate this assumption, but we trust Microsofts             // evaluation. And overall not sending a SameSite value equals to the same             // behavior as SameSite=None for these old versions anyways.             if (userAgent.Contains("Chrome/5") || userAgent.Contains("Chrome/6"))             {                 return true;             }              return false;         }     }

2、Startup配置

public void ConfigureServices(IServiceCollection services)         {             services.AddControllersWithViews();             JwtSecurityTokenHandler.DefaultMapInboundClaims=false;             services.AddAuthentication(options => {                 options.DefaultScheme = "Cookies";                 options.DefaultChallengeScheme = "oidc";                 //options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;             })              .AddCookie("Cookies")              .AddOpenIdConnect("oidc", options => {                  options.Authority = "http://localhost:5001";                  options.ClientId = "sample_mvc_client";                  options.ClientSecret = "sample_client_secret";                  options.ResponseType = "code"; // 隐式授权时不用此段代码                  options.SaveTokens=true;                  options.Scope.Add("sample_api"); // 授权成功之后,如项目中无需访问基于范围认证api可不用此段代码                  options.RequireHttpsMetadata = false; // 不采用https回调              });                          services.ConfigureNonBreakingSameSiteCookies();                      }                   // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.         public void Configure(IApplicationBuilder app, IWebHostEnvironment env)         {             if (env.IsDevelopment())             {                 app.UseDeveloperExceptionPage();             }             else             {                 app.UseExceptionHandler("/Home/Error");             }             app.UseStaticFiles();              app.UseRouting();              // 使用cookie             app.UseCookiePolicy();             // 添加认证中间件             app.UseAuthentication();             app.UseAuthorization();              app.UseEndpoints(endpoints =>             {                 endpoints.MapControllerRoute(                     name: "default",                     pattern: "{controller=Home}/{action=Index}/{id?}");             });         }

3、控制器增加如下方法

/// <summary>         /// 登出         /// </summary>         /// <returns></returns>         public IActionResult LogOut()         {             return SignOut("Cookies","oidc");         }          /// <summary>         /// 模拟请求api         /// </summary>         /// <returns></returns>         public async Task<IActionResult> CallApi()         {             // 获取访问令牌             var accessToken = await HttpContext.GetTokenAsync("access_token");             // 创建HTTP客户端             var client = new HttpClient();             // 设置授权请求头             client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);             // 请求API             var content = await client.GetStringAsync("http://localhost:5000/IdentityServer");             // 转换api返回结果             ViewBag.Josn = JArray.Parse(content).ToString();             return View();         }          [Authorize]         public IActionResult Privacy()         {             return View();         }

4、Privacy.cshtml页面配置

<div><a href='@Url.Action("LogOut")'>登出</a>&nbsp;&nbsp;&nbsp; <a href='@Url.Action("CallApi")'>模拟请求api</a></div> <br /> <br /> <dl>     用户信息     @foreach (var claim in User.Claims)     {         <dt>@claim.Type</dt>         <dt>@claim.Value</dt>     } </dl>  <dl>     认证信息     @foreach (var prop in (await Context.AuthenticateAsync()).Properties.Items)     {         <dt>@prop.Key</dt>         <dt>@prop.Value</dt>     }  </dl>

五、项目运行效果

1、同时启动API、QuickStartIdentityServer4、Sample.MvcClient

 Identity Server 4使用OpenID Connect添加用户身份验证(三)

 

 Identity Server 4使用OpenID Connect添加用户身份验证(三)

 

 

Identity Server 4使用OpenID Connect添加用户身份验证(三)

 

 

Identity Server 4使用OpenID Connect添加用户身份验证(三)

 

 

 

 

 2、登出

Identity Server 4使用OpenID Connect添加用户身份验证(三)

 

 Identity Server 4使用OpenID Connect添加用户身份验证(三)

 

 3、模拟请求api

Identity Server 4使用OpenID Connect添加用户身份验证(三)

 

 Identity Server 4使用OpenID Connect添加用户身份验证(三)

 

发表评论

相关文章