Asp Net Core開發(fā)筆記之如何給SwaggerUI加上登錄保護功能
前言
在 SwaggerUI 中加入登錄驗證,是我很早前就做過的,不過之前的做法總感覺有點硬編碼,最近 .Net8 增加了一個新特性:調(diào)用 MapSwagger().RequireAuthorization
來保護 Swagger UI ,但官方的這個功能又像半成品一樣,只能使用 postman curl 之類的工具帶上 Authorization
header 來請求,在瀏覽器里打開就直接401了 ……
剛好有個項目需要用到這個功能,于是我把之前做過的 SwaggerUI 登錄認證中間件拿出來重構(gòu)了一下。
這次我依然使用 Basic Auth 的方式來登錄,寫了一個自定義的 SwaggerAuthenticationHandler
,通過 Microsoft.AspNetCore.Authentication
提供的擴展方法來實現(xiàn)登錄。
PS:本文以我最近在開發(fā)的單點認證項目(IdentityServerLite)為例
配置Swagger
這次我試著不按照寫代碼的順序,而是站在使用者的角度來介紹,也許會更直觀一些。
編輯 src/IdsLite.Api/Extensions/CfgSwagger.cs
文件 (顧名思義,這是用來配置Swagger的相關(guān)擴展方法)
public static class CfgSwagger { public static IServiceCollection AddSwagger(this IServiceCollection services) { services.AddSwaggerGen(); return services; } public static IApplicationBuilder UseSwaggerWithAuthorize(this IApplicationBuilder app) { app.UseMiddleware<SwaggerBasicAuthMiddleware>(); app.UseSwagger(); app.UseSwaggerUI(); return app; } }
其他的都是常規(guī)的配置,重點在于 app.UseMiddleware<SwaggerBasicAuthMiddleware>();
添加了一個中間件
SwaggerBasicAuth 中間件
來編寫這個中間件,代碼路徑 src/IdsLite.Api/Middlewares/SwaggerBasicAuthMiddleware.cs
public class SwaggerBasicAuthMiddleware { private readonly RequestDelegate _next; public SwaggerBasicAuthMiddleware(RequestDelegate next) { _next = next; } public async Task InvokeAsync(HttpContext context) { if (context.Request.Path.StartsWithSegments("/swagger")) { var result = await context.AuthenticateAsync(AuthSchemes.Swagger); if (!result.Succeeded) { context.Response.Headers["WWW-Authenticate"] = "Basic"; context.Response.StatusCode = StatusCodes.Status401Unauthorized; return; } } await _next(context); } }
主要邏輯在 InvokeAsync
方法里
判斷當前地址以 /swagger
開頭的話,就進入身份認證流程,如果配置了其他 SwaggerUI 地址,記得同步修改這個中間件的配置,或者做成通用的配置,避免硬編碼。
這里使用了 Microsoft.AspNetCore.Authentication.AuthenticationHttpContextExtensions
提供的擴展方法 context.AuthenticateAsync("Scheme Name")
來驗證身份 (具體的 scheme 我們后面會實現(xiàn))
如果驗證失敗的話,返回 401 ,同時添加響應(yīng)頭 WWW-Authenticate:Basic
,這樣就能在瀏覽器里彈出輸入用戶名和密碼的提示框了。
AuthenticationScheme
在注冊 Authentication
服務(wù)的時候,可以添加一些其他的 scheme
PS: AspNetCore 的這套 Identity 確實有點復(fù)雜,用了這么久感覺還是沒有系統(tǒng)的認識這個 Identity 框架
注冊服務(wù)
注冊服務(wù)的代碼大概是這樣
services .AddAuthentication(options => { options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultSignInScheme = JwtBearerDefaults.AuthenticationScheme; }) .AddJwtBearer(...) .AddScheme<AuthenticationSchemeOptions, SwaggerAuthenticationHandler>(AuthSchemes.Swagger, null);
AddScheme
方法可以添加各種類型的認證方案,這里添加了一個自定義的認證方案 SwaggerAuthenticationHandler
,后面的參數(shù)是方案的名稱和選項。
為了避免硬編碼,我寫了個靜態(tài)類
public static class AuthSchemes { public const string Swagger = "SwaggerAuthentication"; }
SwaggerAuthenticationHandler
接下來實現(xiàn)這個自定義的認證方案
其實就是把 Basic Authenticate 和固定用戶名和密碼結(jié)合在一起
不過為了不在代碼里硬編碼,我把用戶名和密碼放在配置里了,通過注入 IOption<T>
的方式獲取。也可以放在數(shù)據(jù)庫里,通過 EFCore 之類的去讀取。
public class SwaggerAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions> { public SwaggerAuthenticationHandler(IOptionsMonitor<AuthenticationSchemeOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock) {} public SwaggerAuthenticationHandler(IOptionsMonitor<AuthenticationSchemeOptions> options, ILoggerFactory logger, UrlEncoder encoder) : base(options, logger, encoder) {} protected override async Task<AuthenticateResult> HandleAuthenticateAsync() { if (!Request.Headers.TryGetValue("Authorization", out var value)) { return AuthenticateResult.Fail("Missing Authorization Header"); } var config = Context.RequestServices.GetRequiredService<IOptions<IdsLiteConfig>>().Value; try { var authHeader = AuthenticationHeaderValue.Parse(value); var credentialBytes = Convert.FromBase64String(authHeader.Parameter); var credentials = Encoding.UTF8.GetString(credentialBytes).Split(":", 2); var username = credentials[0]; var password = credentials[1]; if (username != config.Swagger.UserName || password != config.Swagger.Password) { return AuthenticateResult.Fail("Invalid Username or Password"); } var claims = new[] { new Claim(ClaimTypes.Name, username) }; var identity = new ClaimsIdentity(claims, Scheme.Name); var principal = new ClaimsPrincipal(identity); var ticket = new AuthenticationTicket(principal, Scheme.Name); return AuthenticateResult.Success(ticket); } catch { return AuthenticateResult.Fail("Invalid Authorization Header"); } } }
try
里面的代碼,就是從 request header 里讀取 basic auth 的用戶名和密碼(通常是 Base64 編碼過的),解碼之后判斷是否正確,然后返回認證結(jié)果。
擴展
還可以集成 OpenIDConnect 和 OAuth ,我還沒有實踐,詳情見參考資料。
小結(jié)
既要在項目發(fā)布后訪問 SwaggerUI ,又要保證一定的安全性,本文提供的思路或許是一種比較簡單又有效的解決方案。
參考資料
- https://medium.com/@niteshsinghal85/securing-swagger-in-production-92d0a045a5
- https://medium.com/@niteshsinghal85/securing-swagger-ui-in-production-in-asp-net-core-part-2-dc2ae0f03c73
到此這篇關(guān)于Asp Net Core開發(fā)筆記之如何給SwaggerUI加上登錄保護功能的文章就介紹到這了,更多相關(guān)Asp Net Core SwaggerUI登錄保護內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Asp.net TreeView來構(gòu)建用戶選擇輸入的方法 推薦
選擇優(yōu)于輸入,這是一般人的共識,面對繁多的數(shù)據(jù),提供良好的選擇界面,一方面增強用戶的界面體驗,一方面也提高了數(shù)據(jù)的準確性,更節(jié)省了用戶的寶貴時間。2009-12-12subsonic3.0插件更新字符串過長引發(fā)的異常修復(fù)方法
這篇文章主要介紹了subsonic3.0插件更新字符串過長引發(fā)的異常修復(fù)方法,需要的朋友可以參考下2014-04-04ASP.NET中CKEditor與CKFinder的配置使用
這篇文章主要介紹了ASP.NET中CKEditor與CKFinder的配置使用的相關(guān)資料,需要的朋友可以參考下2015-06-06Ibatis.net結(jié)合oracle批量刪除實現(xiàn)代碼
本文介紹Ibatis.net結(jié)合oracle實現(xiàn)批量刪除寫法,并提供簡單的示例代碼供參考2012-12-12asp.net core web api項目添加自定義中間件的實現(xiàn)
ASP.NET Core Web API項目中可以通過自定義中間件來對請求進行時間戳校驗,本文就來介紹一下項目添加自定義中間件的實現(xiàn),具有一定的參考價值,感興趣的可以了解一下2025-01-01