Swashbuckle.AspNetCore3.0 介绍

一个使用 ASP.NET Core 构建的 API 的 Swagger 工具。直接从您的路由,控制器和模型生成漂亮的 API 文档,包括用于探索和测试操作的 UI。

项目主页:https://github.com/domaindrivendev/Swashbuckle.AspNetCore

划重点,使用多看看 Readme,然后看下项目官方示例,遇到问题找找 issues

继上篇Swashbuckle.AspNetCore3.0 的二次封装与使用分享了二次封装的代码,本篇将分享如何给文档添加一个登录页,控制文档的访问权限(文末附完整 Demo)

关于生产环境接口文档的显示

在此之前的接口项目中,若使用了 Swashbuckle.AspNetCore,都是控制其只在开发环境使用,不会就这样将其发布到生产环境(安全第一) 。

那么,怎么安全的发布 swagger 呢?我有两种想法

  • 将路由前缀改得超级复杂
  • 添加一个拦截器控制 swagger 文档的访问必须获得授权(登录)

大佬若有更好的想法,还望指点一二

下面我将介绍基于 asp.net core2.1 且使用了 Swashbuckle.AspNetCore3.0 的项目种是怎么去实现安全校验的

通过本篇文章之后,可以放心的将项目中的 swagger 文档发布到生产环境,并使其可通过用户名密码去登录访问,得以安全且方便的测试接口。

实现思路

前面已经说到,需要一个拦截器,而这个拦截器还需要是全局的,在 asp.net core 中,自然就需要用到的是中间件

步骤如下,在 UseSwagger 之前使用自定义的中间件

拦截所有 swagger 相关请求,判断是否授权登录

若未登录则跳转到授权登录页,登录后即可访问 swagger 的资源

如果项目本身有登录系统,可在自定义中间件中使用项目中的登录,

没有的话,我会分享一个简单的用户密码登录的方案

Demo 如下图所示

为使用 Swashbuckle.AspNetCore3 的项目添加接口文档登录功能

在写此功能之前,已经封装了一部分代码,此功能算是在此之前的代码封装的一部分,不过是后面完成的。文中代码删除了耦合,和 demo 中会有一点差异。

定义模型存放用户密码

    public class CustomSwaggerAuth
{
public CustomSwaggerAuth() { }
public CustomSwaggerAuth(string userName,string userPwd)
{
UserName = userName;
UserPwd = userPwd;
}
public string UserName { get; set; }
public string UserPwd { get; set; }
//加密字符串
public string AuthStr
{
get
{
return SecurityHelper.HMACSHA256(UserName + UserPwd);
}
}
}

加密方法(HMACSHA256)

    public static string HMACSHA256(string srcString, string key="abc123")
{
byte[] secrectKey = Encoding.UTF8.GetBytes(key);
using (HMACSHA256 hmac = new HMACSHA256(secrectKey))
{
hmac.Initialize(); byte[] bytes_hmac_in = Encoding.UTF8.GetBytes(srcString);
byte[] bytes_hamc_out = hmac.ComputeHash(bytes_hmac_in); string str_hamc_out = BitConverter.ToString(bytes_hamc_out);
str_hamc_out = str_hamc_out.Replace("-", ""); return str_hamc_out;
}
}

自定义中间件

此中间件中有使用的 login.html,其属性均为内嵌资源,故事用 GetManifestResourceStream 读取文件流并输出,这样可以方便的将其进行封装到独立的类库中,而不与输出项目耦合

关于退出按钮,可以参考前文自定义 index.html

    private const string SWAGGER_ATUH_COOKIE = nameof(SWAGGER_ATUH_COOKIE);
public void Configure(IApplicationBuilder app)
{
var options=new {
RoutePrefix="swagger",
SwaggerAuthList = new List<CustomSwaggerAuth>()
{
new CustomSwaggerAuth("swaggerloginer","123456")
},
}
var currentAssembly = typeof(CustomSwaggerAuth).GetTypeInfo().Assembly;
app.Use(async (context, next) =>
{
var _method = context.Request.Method.ToLower();
var _path = context.Request.Path.Value;
// 非swagger相关请求直接跳过
if (_path.IndexOf($"/{options.RoutePrefix}") != 0)
{
await next();
return;
}
else if (_path == $"/{options.RoutePrefix}/login.html")
{
//登录
if (_method == "get")
{
//读取CustomSwaggerAuth所在程序集内嵌的login.html并输出
var stream = currentAssembly.GetManifestResourceStream($"{currentAssembly.GetName().Name}.login.html");
byte[] buffer = new byte[stream.Length];
stream.Read(buffer, 0, buffer.Length);
context.Response.ContentType = "text/html;charset=utf-8";
context.Response.StatusCode = StatusCodes.Status200OK;
context.Response.Body.Write(buffer, 0, buffer.Length);
return;
}
else if (_method == "post")
{
var userModel = new CustomSwaggerAuth(context.Request.Form["userName"], context.Request.Form["userPwd"]);
if (!options.SwaggerAuthList.Any(e => e.UserName == userModel.UserName && e.UserPwd == userModel.UserPwd))
{
await context.Response.WriteAsync("login error!");
return;
}
//context.Response.Cookies.Append("swagger_auth_name", userModel.UserName);
context.Response.Cookies.Append(SWAGGER_ATUH_COOKIE, userModel.AuthStr);
context.Response.Redirect($"/{options.RoutePrefix}");
return;
}
}
else if (_path == $"/{options.RoutePrefix}/logout")
{
//退出
context.Response.Cookies.Delete(SWAGGER_ATUH_COOKIE);
context.Response.Redirect($"/{options.RoutePrefix}/login.html");
return;
}
else
{
//若未登录则跳转登录
if (!options.SwaggerAuthList.Any(s => !string.IsNullOrEmpty(s.AuthStr) && s.AuthStr == context.Request.Cookies[SWAGGER_ATUH_COOKIE]))
{
context.Response.Redirect($"/{options.RoutePrefix}/login.html");
return;
}
}
await next();
});
app.UseSwagger();
app.UseSwaggerUI(c=>{
if (options.SwaggerAuthList.Count > 0)
{
//index.html中添加ConfigObject属性
c.ConfigObject["customAuth"] = true;
c.ConfigObject["loginUrl"] = $"/{options.RoutePrefix}/login.html";
c.ConfigObject["logoutUrl"] = $"/{options.RoutePrefix}/logout";
}
});
app.UseMvc();
}

index.html 添加退出按钮

if (configObject.customAuth) {
var logOutEle = document.createElement('button')
logOutEle.className = 'btn '
logOutEle.innerText = '退出'
logOutEle.onclick = function() {
location.href = configObject.logoutUrl
}
document.getElementsByClassName('topbar-wrapper')[0].appendChild(logOutEle)
}

自定义的 index.html,login.html

注意:需要将其改为内嵌资源(属性->生成操作->嵌入的资源)

完整 Demo 下载

在asp.net core2.1中添加中间件以扩展Swashbuckle.AspNetCore3.0支持简单的文档访问权限控制的更多相关文章

  1. ASP.NET在主题中添加CSS文件

    ASP.NET在主题中添加CSS文件 在ASP.NET中,可以使用CSS来控制页面上HTML元素和ASP.NET控件的皮肤.如果在主题文件夹中添加了CSS文件,则在页面应用主题时也会自动应用CSS. ...

  2. 006.Adding a controller to a ASP.NET Core MVC app with Visual Studio -- 【在asp.net core mvc 中添加一个控制器】

    Adding a controller to a ASP.NET Core MVC app with Visual Studio 在asp.net core mvc 中添加一个控制器 2017-2-2 ...

  3. 008.Adding a model to an ASP.NET Core MVC app --【在 asp.net core mvc 中添加一个model (模型)】

    Adding a model to an ASP.NET Core MVC app在 asp.net core mvc 中添加一个model (模型)2017-3-30 8 分钟阅读时长 本文内容1. ...

  4. 007.Adding a view to an ASP.NET Core MVC app -- 【在asp.net core mvc中添加视图】

    Adding a view to an ASP.NET Core MVC app 在asp.net core mvc中添加视图 2017-3-4 7 分钟阅读时长 本文内容 1.Changing vi ...

  5. ASP.Net Core2.1中的HttpClientFactory系列一:HttpClient的缺陷

    引言: ASP.NET Core2.1 中出现了一个新的 HttpClientFactory 功能, 它有助于解决开发人员在使用 HttpClient 实例从其应用程序中访问外部 web 资源时可能遇 ...

  6. JAVA中让Swagger产出更加符合我们诉求的描述文档,按需决定显示或者隐藏指定内容

    大家好,又见面啦. 在前一篇文档<JAVA中自定义扩展Swagger的能力,自动生成参数取值含义说明,提升开发效率>中,我们探讨了如何通过自定义注解的方式扩展swagger的能力让Swag ...

  7. (转)浅析Java中的访问权限控制

    原文地址: http://www.cnblogs.com/dolphin0520/p/3734915.html 今天我们来一起了解一下Java语言中的访问权限控制.在讨论访问权限控制之前,先来讨论一下 ...

  8. 浅析Java中的访问权限控制

    浅析Java中的访问权限控制 今天我们来一起了解一下Java语言中的访问权限控制.在讨论访问权限控制之前,先来讨论一下为何需要访问权限控制.考虑两个场景: 场景1:工程师A编写了一个类ClassA,但 ...

  9. C++中public/protect/private三种访问权限控制

    一.成员访问权限控制 1.public (1)public成员变量可以被成员函数访问  [访问性] (2)public成员可以被实体对象访问  [访问性] (3)public成员可以成为子类成员  [ ...

随机推荐

  1. Idea的一些调试技巧

    程序员的工作内容,除了大部分时间写代码之外,因为有不少的时间是用在调试代码上.甚至说不是在调试代码,就是即将调试代码. :) 今天我们来谈谈调试代码的一些技巧,在使用IDE提供的debugger时一些 ...

  2. async 和 await 之异步编程的学习

    async修改一个方法,表示其为异步方法.而await表示等待一个异步任务的执行.js方面,在es7中开始得以支持:而.net在c#5.0开始支持.本文章将分别简单介绍他们在js和.net中的基本用法 ...

  3. app后端设计(10)--数据增量更新

    在新浪微博的app中,从别的页面进入主页,在没有网络的情况下,首页中的已经收到的微博还是能显示的,这显然是把相关的数据存储在app本地. 使用数据的app本地存储,能减少网络的流量,同时极大提高了用户 ...

  4. 好代码是管出来的——浅谈.Net Core的代码管理方法与落地(更新中...)

    软件开发的目的是在规定成本和时间前提下,开发出具有适用性.有效性.可修改性.可靠性.可理解性.可维护性.可重用性.可移植性.可追踪性.可互操作性和满足用户需求的软件产品. 而对于整个开发过程来说,开发 ...

  5. 阿里巴巴的开源项目Druid(关于数据库连接)

    1 配置 和dbcp类似,druid的常用配置项如下 配置 缺省值 说明 name   配置这个属性的意义在于,如果存在多个数据源,监控的时候可以通过名字来区分开来.如果没有配置,将会生成一个名字,格 ...

  6. noip前集训

    10.18 关网了,2333 上午考试,130 rank16 一直在刚T2的割点,却直接弃了一道第一眼看上去不可做但实际并没那么难想的小模拟 但是T2没搞出来是不是也要反思一下,先是割点板子忘了,之后 ...

  7. bzoj 3126 单调队列优化dp

    能转移的最左是其左边完整区间的最右左端点,最右是能覆盖它的最左左端点-1 #pragma GCC optimize ("O3") #include<cstdio> #i ...

  8. BZOJ_2440_[中山市选2011]完全平方数_容斥原理+线性筛

    BZOJ_2440_[中山市选2011]完全平方数_容斥原理 题意: 求第k个不是完全平方数倍数的数 分析: 二分答案,转化成1~x中不是完全平方数倍数的数的个数 答案=所有数-1个质数的平方的倍数+ ...

  9. Https协议与HttpClient的实现

    一.背景 HTTP是一个传输内容有可读性的公开协议,客户端与服务器端的数据完全通过明文传输.在这个背景之下,整个依赖于Http协议的互联网数据都是透明的,这带来了很大的数据安全隐患.想要解决这个问题有 ...

  10. 哎呀,我老大写Bug啦——记一次MessageQueue的优化

    MessageQueue,顾名思义消息队列,在系统开发中也是用的比较多的一个中间件吧.我们这里主要用它来做日志管理和订单管理的,记得老老大(恩,是的,就是老老大,因为他已经跳槽了)还在的时候,当时也是 ...