首页 > 编程语言 >Asp-Net-Core开发笔记:给SwaggerUI加上登录保护功能

Asp-Net-Core开发笔记:给SwaggerUI加上登录保护功能

时间:2024-05-21 16:58:58浏览次数:32  
标签:Core SwaggerUI return options Asp context var Swagger public

前言#

在 SwaggerUI 中加入登录验证,是我很早前就做过的,不过之前的做法总感觉有点硬编码,最近 .Net8 增加了一个新特性:调用 MapSwagger().RequireAuthorization 来保护 Swagger UI ,但官方的这个功能又像半成品一样,只能使用 postman curl 之类的工具带上 Authorization header 来请求,在浏览器里打开就直接401了 ……

刚好有个项目需要用到这个功能,于是我把之前做过的 SwaggerUI 登录认证中间件拿出来重构了一下。

这次我依然使用 Basic Auth 的方式来登录,写了一个自定义的 SwaggerAuthenticationHandler,通过 Microsoft.AspNetCore.Authentication 提供的扩展方法来实现登录。

PS:本文以我最近在开发的单点认证项目(IdentityServerLite)为例

配置Swagger#

这次我试着不按照写代码的顺序,而是站在使用者的角度来介绍,也许会更直观一些。

编辑 src/IdsLite.Api/Extensions/CfgSwagger.cs 文件 (顾名思义,这是用来配置Swagger的相关扩展方法)

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;
  }
}

其他的都是常规的配置,重点在于 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 我们后面会实现)

如果验证失败的话,返回 401 ,同时添加响应头 WWW-Authenticate:Basic ,这样就能在浏览器里弹出输入用户名和密码的提示框了。

AuthenticationScheme#

在注册 Authentication 服务的时候,可以添加一些其他的 scheme

PS: AspNetCore 的这套 Identity 确实有点复杂,用了这么久感觉还是没有系统的认识这个 Identity 框架

注册服务#

注册服务的代码大概是这样

services
  .AddAuthentication(options => {
    options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultSignInScheme = JwtBearerDefaults.AuthenticationScheme;
  })
  .AddJwtBearer(...)
  .AddScheme<AuthenticationSchemeOptions, SwaggerAuthenticationHandler>(AuthSchemes.Swagger, null);

AddScheme 方法可以添加各种类型的认证方案,这里添加了一个自定义的认证方案 SwaggerAuthenticationHandler,后面的参数是方案的名称和选项。

为了避免硬编码,我写了个静态类

public static class AuthSchemes {
  public const string Swagger = "SwaggerAuthentication";
}

SwaggerAuthenticationHandler#

接下来实现这个自定义的认证方案

其实就是把 Basic Authenticate 和固定用户名和密码结合在一起

不过为了不在代码里硬编码,我把用户名和密码放在配置里了,通过注入 IOption<T> 的方式获取。也可以放在数据库里,通过 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 编码过的),解码之后判断是否正确,然后返回认证结果。

扩展#

还可以集成 OpenIDConnect 和 OAuth ,我还没有实践,详情见参考资料。

小结#

既要在项目发布后访问 SwaggerUI ,又要保证一定的安全性,本文提供的思路或许是一种比较简单又有效的解决方案。

参考资料#

 

2024-05-21 16:51:16【出处】:https://www.cnblogs.com/deali/p/18204365

=======================================================================================

标签:Core,SwaggerUI,return,options,Asp,context,var,Swagger,public
From: https://www.cnblogs.com/mq0036/p/18204460

相关文章

  • Asp-Net-Core开发笔记:使用ActionFilterAttribute实现非侵入式的参数校验
    前言#在现代应用开发中,确保API的安全性和可靠性至关重要。面向切面编程(AOP)通过将横切关注点(如验证、日志记录、异常处理)与核心业务逻辑分离,极大地提升了代码的模块化和可维护性。在ASP.NETCore中,利用ActionFilterAttribute可以方便地实现AOP的理念,能够以简洁、高效的方式进行......
  • Sitecore 设置 SelectItems 最大长度问题
    添加一个configpatch文件即可,然后放在App_Config下,内容如下:sitecore默认value是100。<?xmlversion="1.0"encoding="utf-8"?><configurationxmlns:patch="http://www.sitecore.net/xmlconfig/"xmlns:role="http://www.sitecore.n......
  • YiShaAdmin:一款基于.NET Core Web + Bootstrap的企业级快速开发框架
    前言今天大姚给大家分享一款基于.NETCoreWeb+Bootstrap的企业级快速后台开发框架、权限管理系统,代码简单易懂、界面简洁美观(基于MITLicense开源,免费可商用):YiShaAdmin。项目官方介绍YiShaAdmin基于.NETCoreWeb开发,借鉴了很多开源项目的优点,让你开发Web管理系统和移动端A......
  • .net8 winform程序使用EntityFrameworkCore连接数据库
    在.NET8WinForms应用程序中使用EntityFramework(EF)Core,你需要按照以下步骤操作:1.添加EntityFrameworkCoreNuGet包。2.定义你的数据模型。3.创建数据库上下文(DbContext)。4.在数据库上下文中配置EntityFramework。5.使用EntityFrameworkCore的API来执行数据库操作。......
  • C# webform 在aspx页面调用aspx.cs页面的方法
    前台代码--调用后台的GetEcharts1方法,并传入三个参数startDateValue,ipEndDate,ddlTypeValue<inputtype="button"id="loadExce2l"value="查看"onclick="loadEcharts()"/>functionloadEcharts(){console.log(19999999999)......
  • Liunx部署NetCore,接口获取mysql本地数据时报:The SSL connection could not be establ
    今天将 NETCore程序发布到一台新的服务器,程序中有https请求第三方的Api,但是报了如下的错误:TheSSLconnectioncouldnotbeestablished,seeinnerexception解决办法下面命令查询openssl的路径opensslversion-a然后将CentOS默认的opensslCA证书拷贝到OPENSSLD......
  • netcore6 发布到linux centos7 踩坑记录
    具体dotnet6部署的过程,用守护进程。参考:https://blog.csdn.net/qq_45602658/article/details/129299814还有解决出现dotnet:/lib64/libstdc++.so.6:versionGLIBCXX_3.4.21&#x27;notfound(requiredbydotnet)dotnet:/lib64/libstdc++.so.6:未找到版本GLIBCXX_3.4.21(dot......
  • VUE,HbuilderX开发H5页面,配置调试,部署以及JWT,Token。调用本机netcore接口
    花了一周时间,测试了各种方式。对于VUE开发H5页面,然后部署到Nginx服务。再调用本地的HTTPS接口。1、本地开发及使用IE或Chrome进行开发调试,并调用本地接口同步开发。本地Netcore,开发按原有方式进行。Hbuilderx,开发并调用本地接口。设置时,不要使用Https进行。直接用http即可。Hbuild......
  • Liunx下通过netcore接口生成前端图片的问题。
    用netcore来生成前端微信Native支付的二维码。1、首先CentOS7.0要安装libgdiplus,命令如下:yuminstalllibgdiplus-devel,然后重启netcore服务。//这个地方要注意,网上有不少例子的下载命令是错的,有的时候安装不上。2、Vs代码使用QRCoder库,代码如下publicstaticMemoryStream......
  • django rest django.core.exceptions.ImproperlyConfigured: Could not resolve URL f
    使用:HyperlinkedRelatedField报错信息django.core.exceptions.ImproperlyConfigured:CouldnotresolveURLforhyperlinkedrelationshipusingviewname"fk_table:album_list".YoumayhavefailedtoincludetherelatedmodelinyourAPI,orincorrectly......