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 { /// - /// 浏览器认证 处理器 + /// 浏览器认证处理器:基于接口实现 /// 可实现子接口 IAuthenticationRequestHandler, 以控制后续中间件是否执行. /// - public class BrowserAuthenticationHandler2 : + public class BrowserAuthenticationBaseHandler : IAuthenticationHandler, IAuthenticationRequestHandler, IAuthenticationSignInHandler, IAuthenticationSignOutHandler - where TOptions : AuthenticationSchemeOptions, new() { public string DefaultSchemeName = BrowserAuthenticationDefault.SchemeName; public HttpContext? CurrentHttpContext; - public List AllowBrowsers = BrowserAuthenticationDefault.AllowBrowsers; + public BrowserAuthenticationOptions Options; - public BrowserAuthenticationOptions Options = BrowserAuthenticationDefault.DefaultOptions; - - public BrowserAuthenticationHandler2() + public BrowserAuthenticationBaseHandler(BrowserAuthenticationOptions option) { - + Options = option; } /// @@ -65,7 +62,7 @@ namespace AuthStudy.Authentication.Browser ClientInfo clientInfo = Parser.GetDefault().Parse(userAgent); //移动设备认证 - if (!Options.AllowMobile && BrowserAuthenticationHandler2.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 + var claims = new List { 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 } /// - /// 无认证:服务端向客户端(浏览器)发质询(要求提供一个新票据),质询体现为 htpp请求的响应。 + /// 无认证:服务端向客户端(浏览器)发质询(要求提供一个新票据),质询体现为 http请求的响应。 /// 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 } /// - /// 无权限:服务端向客户端(浏览器)发质询(要求提供一个新票据),质询体现为 htpp请求的响应。 + /// 无权限:服务端向客户端(浏览器)发质询(要求提供一个新票据),质询体现为 http请求的响应。 /// 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 /// /// 初始化 /// - 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(AuthenticationSchemeName, AuthenticationDispalyName); + options.AddScheme(AuthenticationSchemeName, AuthenticationDispalyName); }); return builder; @@ -47,9 +48,14 @@ namespace AuthStudy.Authentication.Browser { BrowserAuthenticationOptions defaultOption = option ?? new(); builder.AddSingleton(defaultOption); - builder.AddSingleton(); + builder.AddSingleton(); 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 { /// - /// 浏览器认证 处理器 - /// 可实现子接口 IAuthenticationRequestHandler, 以控制后续中间件是否执行. + /// 浏览器认证处理器:基于默认类型实现 /// - public class BrowserAuthenticationHandler : - IAuthenticationHandler, - IAuthenticationRequestHandler, - IAuthenticationSignInHandler, - IAuthenticationSignOutHandler + public class BrowserAuthenticationHandler : AuthenticationHandler + { public string DefaultSchemeName = BrowserAuthenticationDefault.SchemeName; - public HttpContext? CurrentHttpContext; - public BrowserAuthenticationOptions Options; - - public BrowserAuthenticationHandler(BrowserAuthenticationOptions option) + public BrowserAuthenticationHandler + ( + IOptionsMonitor options, + ILoggerFactory logger, + UrlEncoder encoder, + ISystemClock clock + ) : base(options, logger, encoder, clock) { - Options = option; + } /// /// 认证 /// - public Task AuthenticateAsync() + protected override Task 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 + var Claims = new List { 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 } /// - /// 无认证:服务端向客户端(浏览器)发质询(要求提供一个新票据),质询体现为 http请求的响应。 + /// 质询:服务端向客户端(浏览器)发质询(要求提供一个新票据),质询体现为 htpp请求的响应。 /// - 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; - } - - /// - /// 无权限:服务端向客户端(浏览器)发质询(要求提供一个新票据),质询体现为 http请求的响应。 - /// - 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; - } - - /// - /// IAuthenticationRequestHandler - /// 返回true,立即反回,不执行后续中间件 - /// - public Task HandleRequestAsync() - { - return Task.FromResult(false); - } - - /// - /// 初始化 - /// - 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; - } - - /// - /// 登陆方法 - /// 写入Cookie和Session,认证信息持久化等 - /// - public Task SignInAsync(ClaimsPrincipal user, AuthenticationProperties? properties) - { - //导航到主页 - //CurrentHttpContext?.Response.Redirect("/api/app/index"); - - return Task.CompletedTask; - } - - /// - /// 退出方法: 反操作登陆方法 - /// 清除Cookie和Session,删除认证信息的持久化,作废票据等 - /// - 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() { "Edge" } } ); + builder.Services + .AddAuthentication("BrowserAuthenticationHandlerByBase") + .AddScheme("BrowserAuthenticationHandlerByBase", t => + { + t.AllowBrowsers = new List() { "Edge", "Chrome", "Firefox" }; + }); + //默认基类实现注册 + #endregion WebApplication app = builder.Build();