From 0c1ebef1b3503becee10493b20f85393379b962d Mon Sep 17 00:00:00 2001
From: bicijinlian <bicijinlian@163.com>
Date: Mon, 12 Jun 2023 12:13:28 +0800
Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B5=8F=E8=A7=88=E5=99=A8=E8=AE=A4?=
 =?UTF-8?q?=E8=AF=81?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 ...cs => BrowserAuthenticationBaseHandler.cs} |  37 +++---
 .../BrowserAuthenticationExtensions.cs        |  10 +-
 .../BrowserAuthenticationHandler.cs           | 106 +++++-------------
 .../Controllers/AccountsController.cs         |   4 +-
 AuthStudy.WebApp/Program.cs                   |  11 ++
 5 files changed, 64 insertions(+), 104 deletions(-)
 rename AuthStudy.Authentication.Browser/{BrowserAuthenticationHandler2.cs => BrowserAuthenticationBaseHandler.cs} (85%)

diff --git a/AuthStudy.Authentication.Browser/BrowserAuthenticationHandler2.cs b/AuthStudy.Authentication.Browser/BrowserAuthenticationBaseHandler.cs
similarity index 85%
rename from AuthStudy.Authentication.Browser/BrowserAuthenticationHandler2.cs
rename to AuthStudy.Authentication.Browser/BrowserAuthenticationBaseHandler.cs
index e54e55f..29a0cb5 100644
--- a/AuthStudy.Authentication.Browser/BrowserAuthenticationHandler2.cs
+++ b/AuthStudy.Authentication.Browser/BrowserAuthenticationBaseHandler.cs
@@ -17,28 +17,25 @@ using UAParser;
 namespace AuthStudy.Authentication.Browser
 {
     /// <summary>
-    /// 浏览器认证 处理器
+    /// 浏览器认证处理器:基于接口实现
     /// 可实现子接口 IAuthenticationRequestHandler, 以控制后续中间件是否执行.
     /// </summary>
-    public class BrowserAuthenticationHandler2<TOptions> :
+    public class BrowserAuthenticationBaseHandler :
         IAuthenticationHandler,
         IAuthenticationRequestHandler,
         IAuthenticationSignInHandler,
         IAuthenticationSignOutHandler
-        where TOptions : AuthenticationSchemeOptions, new()
     {
         public string DefaultSchemeName = BrowserAuthenticationDefault.SchemeName;
 
         public HttpContext? CurrentHttpContext;
 
-        public List<string> AllowBrowsers = BrowserAuthenticationDefault.AllowBrowsers;
+        public BrowserAuthenticationOptions Options;
 
-        public BrowserAuthenticationOptions Options = BrowserAuthenticationDefault.DefaultOptions;
 
-
-        public BrowserAuthenticationHandler2()
+        public BrowserAuthenticationBaseHandler(BrowserAuthenticationOptions option)
         {
-
+            Options = option;
         }
 
         /// <summary>
@@ -65,7 +62,7 @@ namespace AuthStudy.Authentication.Browser
             ClientInfo clientInfo = Parser.GetDefault().Parse(userAgent);
 
             //移动设备认证
-            if (!Options.AllowMobile && BrowserAuthenticationHandler2<TOptions>.IsMobile(clientInfo.UA.Family))
+            if (!Options.AllowMobile && IsMobile(clientInfo.UA.Family))
             {
                 properties.UpdateTokenValue("AuthenticationBrowser", "失败:不被允许的可移动设备");
                 result = AuthenticateResult.Fail($"不被允许的可移动设备:{clientInfo.UA.Family}", properties);
@@ -81,7 +78,7 @@ namespace AuthStudy.Authentication.Browser
             }
 
             //浏览器类型认证
-            if (!AllowBrowsers.Contains(clientInfo.UA.Family))
+            if (!Options.AllowBrowsers.Contains(clientInfo.UA.Family))
             {
                 properties.UpdateTokenValue("AuthenticationBrowser", "失败:不支持的浏览器");
                 result = AuthenticateResult.Fail($"不支持的浏览器:{clientInfo.UA.Family}", properties);
@@ -91,11 +88,11 @@ namespace AuthStudy.Authentication.Browser
 
             //声明(身份项)
             var browser = new Claim("Browser", clientInfo.UA.ToString());       //浏览器
-            var os = new Claim("OS", clientInfo.OS.ToString());            //操作系统
-            var device = new Claim("Device", clientInfo.Device.ToString());    //设备                                                                                           //设备
+            var os = new Claim("OS", clientInfo.OS.ToString());                 //操作系统
+            var device = new Claim("Device", clientInfo.Device.ToString());     //设备                                                                                           //设备
 
             //声明集合
-            var Claims = new List<Claim>
+            var claims = new List<Claim>
             {
                 browser,
                 os,
@@ -103,7 +100,7 @@ namespace AuthStudy.Authentication.Browser
             };
 
             //身份:包含声明集合,是声明集合的包装类,一个身份对应多个声明
-            var claimsIdentity = new ClaimsIdentity(Claims, DefaultSchemeName);
+            var claimsIdentity = new ClaimsIdentity(claims, DefaultSchemeName);
 
             //当事人/主角:是身份Identity的包装,对应多个身份
             var claimsPrincipal = new ClaimsPrincipal(claimsIdentity);
@@ -121,16 +118,16 @@ namespace AuthStudy.Authentication.Browser
         }
 
         /// <summary>
-        /// 无认证:服务端向客户端(浏览器)发质询(要求提供一个新票据),质询体现为 htpp请求的响应。
+        /// 无认证:服务端向客户端(浏览器)发质询(要求提供一个新票据),质询体现为 http请求的响应。
         /// </summary>
         public async Task ChallengeAsync(AuthenticationProperties? properties)
         {
-            properties?.Parameters.Add("x-itme", "无效的认证");
+            properties?.Parameters.Add("x-item", "无效的认证");
 
             CurrentHttpContext!.Response.StatusCode = 401;
             if (CurrentHttpContext?.Response.Body.CanWrite ?? false)
             {
-                var msg = UTF8Encoding.UTF8.GetBytes("认证无效");
+                var msg = Encoding.UTF8.GetBytes("认证无效");
                 await CurrentHttpContext!.Response.Body.WriteAsync(msg);
             }
             CurrentHttpContext?.Items.Add("认证结束时间", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
@@ -138,14 +135,14 @@ namespace AuthStudy.Authentication.Browser
         }
 
         /// <summary>
-        /// 无权限:服务端向客户端(浏览器)发质询(要求提供一个新票据),质询体现为 htpp请求的响应。
+        /// 无权限:服务端向客户端(浏览器)发质询(要求提供一个新票据),质询体现为 http请求的响应。
         /// </summary>
         public async Task ForbidAsync(AuthenticationProperties? properties)
         {
             CurrentHttpContext!.Response.StatusCode = 403;
             if (CurrentHttpContext?.Response.Body.CanWrite ?? false)
             {
-                var msg = UTF8Encoding.UTF8.GetBytes("无权访问");
+                var msg = Encoding.UTF8.GetBytes("无权访问");
                 await CurrentHttpContext!.Response.Body.WriteAsync(msg);
             }
             //return Task.CompletedTask;
@@ -163,7 +160,7 @@ namespace AuthStudy.Authentication.Browser
         /// <summary>
         /// 初始化
         /// </summary>
-        public async Task InitializeAsync(AuthenticationScheme scheme, Microsoft.AspNetCore.Http.HttpContext context)
+        public async Task InitializeAsync(AuthenticationScheme scheme, HttpContext context)
         {
             //初始化工作,传递给认证方法和授权中间件
             CurrentHttpContext = context;
diff --git a/AuthStudy.Authentication.Browser/BrowserAuthenticationExtensions.cs b/AuthStudy.Authentication.Browser/BrowserAuthenticationExtensions.cs
index 18ba244..7de538c 100644
--- a/AuthStudy.Authentication.Browser/BrowserAuthenticationExtensions.cs
+++ b/AuthStudy.Authentication.Browser/BrowserAuthenticationExtensions.cs
@@ -12,6 +12,7 @@ namespace AuthStudy.Authentication.Browser
 {
     public static class BrowserAuthenticationExtensions
     {
+        #region 基于接口的扩展注册
         public static IServiceCollection AddBrowserAuthentication
         (
            this IServiceCollection builder,
@@ -36,7 +37,7 @@ namespace AuthStudy.Authentication.Browser
                 options.DefaultSignInScheme = AuthenticationSchemeName;
                 options.DefaultSignOutScheme = AuthenticationSchemeName;
 
-                options.AddScheme<BrowserAuthenticationHandler>(AuthenticationSchemeName, AuthenticationDispalyName);
+                options.AddScheme<BrowserAuthenticationBaseHandler>(AuthenticationSchemeName, AuthenticationDispalyName);
             });
 
             return builder;
@@ -47,9 +48,14 @@ namespace AuthStudy.Authentication.Browser
         {
             BrowserAuthenticationOptions defaultOption = option ?? new();
             builder.AddSingleton(defaultOption);
-            builder.AddSingleton<BrowserAuthenticationHandler>();
+            builder.AddSingleton<BrowserAuthenticationBaseHandler>();
 
             return builder;
         }
+
+        #endregion
+
+        #region 基于默认基数的扩展注册
+        #endregion
     }
 }
diff --git a/AuthStudy.Authentication.Browser/BrowserAuthenticationHandler.cs b/AuthStudy.Authentication.Browser/BrowserAuthenticationHandler.cs
index d2be67a..bcdeeed 100644
--- a/AuthStudy.Authentication.Browser/BrowserAuthenticationHandler.cs
+++ b/AuthStudy.Authentication.Browser/BrowserAuthenticationHandler.cs
@@ -3,6 +3,7 @@ using System.Security.Claims;
 using System.Security.Policy;
 using System.Security.Principal;
 using System.Text;
+using System.Text.Encodings.Web;
 using System.Text.RegularExpressions;
 using System.Text.Unicode;
 using System.Threading.Tasks.Sources;
@@ -10,6 +11,8 @@ using System.Threading.Tasks.Sources;
 using Microsoft.AspNetCore;
 using Microsoft.AspNetCore.Authentication;
 using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
 using Microsoft.Extensions.Primitives;
 
 using UAParser;
@@ -17,31 +20,30 @@ using UAParser;
 namespace AuthStudy.Authentication.Browser
 {
     /// <summary>
-    /// 浏览器认证 处理器
-    /// 可实现子接口 IAuthenticationRequestHandler, 以控制后续中间件是否执行.
+    /// 浏览器认证处理器:基于默认类型实现
     /// </summary>
-    public class BrowserAuthenticationHandler :
-        IAuthenticationHandler,
-        IAuthenticationRequestHandler,
-        IAuthenticationSignInHandler,
-        IAuthenticationSignOutHandler
+    public class BrowserAuthenticationHandler : AuthenticationHandler<BrowserAuthenticationOptions>
+
     {
         public string DefaultSchemeName = BrowserAuthenticationDefault.SchemeName;
-
         public HttpContext? CurrentHttpContext;
 
-        public BrowserAuthenticationOptions Options;
-
 
-        public BrowserAuthenticationHandler(BrowserAuthenticationOptions option)
+        public BrowserAuthenticationHandler
+        (
+            IOptionsMonitor<BrowserAuthenticationOptions> options,
+            ILoggerFactory logger,
+            UrlEncoder encoder,
+            ISystemClock clock
+        ) : base(options, logger, encoder, clock)
         {
-            Options = option;
+
         }
 
         /// <summary>
         /// 认证
         /// </summary>
-        public Task<AuthenticateResult> AuthenticateAsync()
+        protected override Task<AuthenticateResult> HandleAuthenticateAsync()
         {
             //认证结果
             AuthenticateResult result;
@@ -88,11 +90,11 @@ namespace AuthStudy.Authentication.Browser
 
             //声明(身份项)
             var browser = new Claim("Browser", clientInfo.UA.ToString());       //浏览器
-            var os = new Claim("OS", clientInfo.OS.ToString());                 //操作系统
-            var device = new Claim("Device", clientInfo.Device.ToString());     //设备                                                                                           //设备
+            var os = new Claim("OS", clientInfo.OS.ToString());            //操作系统
+            var device = new Claim("Device", clientInfo.Device.ToString());    //设备                                                                                           //设备
 
             //声明集合
-            var claims = new List<Claim>
+            var Claims = new List<Claim>
             {
                 browser,
                 os,
@@ -100,7 +102,7 @@ namespace AuthStudy.Authentication.Browser
             };
 
             //身份:包含声明集合,是声明集合的包装类,一个身份对应多个声明
-            var claimsIdentity = new ClaimsIdentity(claims, DefaultSchemeName);
+            var claimsIdentity = new ClaimsIdentity(Claims, DefaultSchemeName);
 
             //当事人/主角:是身份Identity的包装,对应多个身份
             var claimsPrincipal = new ClaimsPrincipal(claimsIdentity);
@@ -118,78 +120,19 @@ namespace AuthStudy.Authentication.Browser
         }
 
         /// <summary>
-        /// 无认证:服务端向客户端(浏览器)发质询(要求提供一个新票据),质询体现为 http请求的响应。
+        /// 质询:服务端向客户端(浏览器)发质询(要求提供一个新票据),质询体现为 htpp请求的响应。
         /// </summary>
-        public async Task ChallengeAsync(AuthenticationProperties? properties)
+        protected override Task HandleChallengeAsync(AuthenticationProperties? properties)
         {
-            properties?.Parameters.Add("x-item", "无效的认证");
+            properties?.Parameters.Add("x-itme", "无效的认证");
 
             CurrentHttpContext!.Response.StatusCode = 401;
             if (CurrentHttpContext?.Response.Body.CanWrite ?? false)
             {
-                var msg = Encoding.UTF8.GetBytes("认证无效");
-                await CurrentHttpContext!.Response.Body.WriteAsync(msg);
+                var msg = UTF8Encoding.UTF8.GetBytes("认证无效");
+                var t = CurrentHttpContext!.Response.Body.WriteAsync(msg);
             }
             CurrentHttpContext?.Items.Add("认证结束时间", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
-            //return Task.CompletedTask;
-        }
-
-        /// <summary>
-        /// 无权限:服务端向客户端(浏览器)发质询(要求提供一个新票据),质询体现为 http请求的响应。
-        /// </summary>
-        public async Task ForbidAsync(AuthenticationProperties? properties)
-        {
-            CurrentHttpContext!.Response.StatusCode = 403;
-            if (CurrentHttpContext?.Response.Body.CanWrite ?? false)
-            {
-                var msg = Encoding.UTF8.GetBytes("无权访问");
-                await CurrentHttpContext!.Response.Body.WriteAsync(msg);
-            }
-            //return Task.CompletedTask;
-        }
-
-        /// <summary>
-        /// IAuthenticationRequestHandler
-        /// 返回true,立即反回,不执行后续中间件
-        /// </summary>
-        public Task<bool> HandleRequestAsync()
-        {
-            return Task.FromResult(false);
-        }
-
-        /// <summary>
-        /// 初始化
-        /// </summary>
-        public async Task InitializeAsync(AuthenticationScheme scheme, HttpContext context)
-        {
-            //初始化工作,传递给认证方法和授权中间件
-            CurrentHttpContext = context;
-
-            context.Items.Add("认证初始时间", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
-
-            await Task.CompletedTask;
-        }
-
-        /// <summary>
-        /// 登陆方法
-        /// 写入Cookie和Session,认证信息持久化等
-        /// </summary>
-        public Task SignInAsync(ClaimsPrincipal user, AuthenticationProperties? properties)
-        {
-            //导航到主页
-            //CurrentHttpContext?.Response.Redirect("/api/app/index");
-
-            return Task.CompletedTask;
-        }
-
-        /// <summary>
-        /// 退出方法: 反操作登陆方法
-        /// 清除Cookie和Session,删除认证信息的持久化,作废票据等
-        /// </summary>
-        public Task SignOutAsync(AuthenticationProperties? properties)
-        {
-            //导航到登陆页
-            CurrentHttpContext?.Response.Redirect("/api/auth/login");
             return Task.CompletedTask;
         }
 
@@ -210,5 +153,6 @@ namespace AuthStudy.Authentication.Browser
 
             return isMobile;
         }
+
     }
 }
\ No newline at end of file
diff --git a/AuthStudy.WebApp/Controllers/AccountsController.cs b/AuthStudy.WebApp/Controllers/AccountsController.cs
index f63c2d2..a181931 100644
--- a/AuthStudy.WebApp/Controllers/AccountsController.cs
+++ b/AuthStudy.WebApp/Controllers/AccountsController.cs
@@ -1,3 +1,4 @@
+using AuthStudy.Authentication.Browser;
 using AuthStudy.WebApp.VModels;
 
 using Microsoft.AspNetCore.Authorization;
@@ -15,7 +16,8 @@ namespace AuthStudy.WebApp.Controllers
 
         }
 
-        [Authorize]
+        [Authorize(AuthenticationSchemes = BrowserAuthenticationDefault.SchemeName)]
+        [Authorize(AuthenticationSchemes = "BrowserAuthenticationHandlerByBase")]
         [HttpGet]
         public IActionResult GetAll()
         {
diff --git a/AuthStudy.WebApp/Program.cs b/AuthStudy.WebApp/Program.cs
index abcfb7f..4d6b01d 100644
--- a/AuthStudy.WebApp/Program.cs
+++ b/AuthStudy.WebApp/Program.cs
@@ -1,6 +1,8 @@
 
 using AuthStudy.Authentication.Browser;
 
+using Microsoft.AspNetCore.Components.Forms;
+
 namespace AuthStudy.WebApp
 {
     public class Program
@@ -17,6 +19,7 @@ namespace AuthStudy.WebApp
             builder.Services.AddSwaggerGen();
 
             #region 认证注册
+            //接口实现注册
             builder.Services.AddBrowserAuthentication
             (
                 BrowserAuthenticationDefault.SchemeName,
@@ -26,6 +29,14 @@ namespace AuthStudy.WebApp
                     AllowBrowsers = new List<string>() { "Edge" }
                 }
             );
+            builder.Services
+                .AddAuthentication("BrowserAuthenticationHandlerByBase")
+                .AddScheme<BrowserAuthenticationOptions, BrowserAuthenticationHandler>("BrowserAuthenticationHandlerByBase", t =>
+                {
+                    t.AllowBrowsers = new List<string>() { "Edge", "Chrome", "Firefox" };
+                });
+            //默认基类实现注册
+
             #endregion
 
             WebApplication app = builder.Build();