为CAP面板天添加简单的认证功能 C#|.net

做过后端的比较熟悉,CAP面板有个界面,可以通过域名加cap访问:

但是这个面板直接通过url就可以访问了。

Hangfire Dashboard有自己的面板,可以使用用户名和密码做简单的认证。

LogDashboard也有自己的面板,可以使用用户名和密码做简单的认证。

如下图:

但是CAP的面板是裸露的,没有直接的认证功能。

官方提供了文档,但是没有简单的用户名和密码的认证示例。

https://cap.dotnetcore.xyz/user-guide/zh/monitoring/dashboard/

简单到不知道它在表达什么:

于是只能自己摸索了。

CAP面板引用的包:

DotNetCore.CAP.Dashboard

认证需要另外引用一个包:

Microsoft.AspNetCore.Authentication

然后创建自己的认证处理器:

using System.Net.Http.Headers;
using System.Security.Claims;
using System.Text;
using System.Text.Encodings.Web;
using Microsoft.AspNetCore.Authentication;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

namespace Extensions
{
    /// <summary>
    /// 自定义面板认证
    /// </summary>
    public static class MyAuthDefaults
    {
        /// <summary>
        /// 自定义面板认证-协议名
        /// </summary>
        public const string Policy = "MyAuthPolicy";
        /// <summary>
        /// 自定义面板认证-方案名
        /// </summary>
        public const string Scheme = "MyAuthScheme";
    }

    /// <summary>
    /// 自定义面板认证配置
    /// </summary>
    public class MyAuthSchemeOptions : AuthenticationSchemeOptions { }

    /// <summary>
    /// 自定义面板认证处理器
    /// </summary>
    public class MyAuthHandler : AuthenticationHandler<MyAuthSchemeOptions>
    {
        /// <summary>
        /// 自定义面板认证处理器
        /// </summary>
        public MyAuthHandler(IOptionsMonitor<MyAuthSchemeOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) 
            : base(options, logger, encoder, clock)
        {
        }

        /// <summary>
        /// 自定义面板认证-验证
        /// </summary>
        protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
        {
            AuthenticateResult authResult = null;
            if (!Request.Headers.ContainsKey("Authorization"))
            {
                authResult = AuthenticateResult.NoResult();
                return await Task.FromResult(authResult);
            }
            try
            {
                var authHeader = AuthenticationHeaderValue.Parse(Request.Headers["Authorization"]);
                var credentialsBytes = Convert.FromBase64String(authHeader.Parameter);
                var credentials = Encoding.UTF8.GetString(credentialsBytes).Split(':');
                var username = credentials[0];
                var password = credentials[1];
                if (username != "admin" || password != "123456")
                {
                    authResult = AuthenticateResult.Fail("Invalid Username or Password");
                    return await Task.FromResult(authResult);
                }
                var claims = new[] {
                    new Claim(ClaimTypes.NameIdentifier, username),
                    new Claim(ClaimTypes.Name, username),
                };
                var identity = new ClaimsIdentity(claims, Scheme.Name);
                var principal = new ClaimsPrincipal(identity);
                var ticket = new AuthenticationTicket(principal, Scheme.Name);
                authResult = AuthenticateResult.Success(ticket);
                return await Task.FromResult(authResult);
            }
            catch (Exception ex)
            {
                authResult = AuthenticateResult.Fail("Invalid Authorization Header");
                return await Task.FromResult(authResult);
            }
        }

        /// <summary>
        /// 自定义面板认证-变动
        /// </summary>
        protected override Task HandleChallengeAsync(AuthenticationProperties properties)
        {
            Response.StatusCode = 401;
            Response.Headers["WWW-Authenticate"] = $"Basic realm=\"{Scheme.Name}\", charset=\"UTF-8\"";
            return base.HandleChallengeAsync(properties);
        }
    }
}

代码里面设置了默认账号:

admin/123456

如果账号来自config配置,则需要做如下修改:

using System.Net.Http.Headers;
using System.Security.Claims;
using System.Text;
using System.Text.Encodings.Web;
using Microsoft.AspNetCore.Authentication;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

namespace Extensions
{
    /// <summary>
    /// 自定义面板认证
    /// </summary>
    public static class MyAuthDefaults
    {
        /// <summary>
        /// 自定义面板认证-协议名
        /// </summary>
        public const string Policy = "MyAuthPolicy";
        /// <summary>
        /// 自定义面板认证-方案名
        /// </summary>
        public const string Scheme = "MyAuthScheme";
    }

    /// <summary>
    /// 自定义面板认证配置
    /// </summary>
    public class MyAuthSchemeOptions : AuthenticationSchemeOptions { }

    /// <summary>
    /// 自定义面板认证处理器
    /// </summary>
    public class MyAuthHandler : AuthenticationHandler<MyAuthSchemeOptions>
    {
        private readonly IConfiguration _configuration;

        /// <summary>
        /// 自定义面板认证处理器
        /// </summary>
        public MyAuthHandler(IOptionsMonitor<MyAuthSchemeOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock, IConfiguration configuration) 
            : base(options, logger, encoder, clock)
        {
            _configuration = configuration;
        }

        /// <summary>
        /// 自定义面板认证-验证
        /// </summary>
        protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
        {
            AuthenticateResult authResult = null;
            if (!Request.Headers.ContainsKey("Authorization"))
            {
                authResult = AuthenticateResult.NoResult();
                return await Task.FromResult(authResult);
            }
            try
            {
                var authHeader = AuthenticationHeaderValue.Parse(Request.Headers["Authorization"]);
                var credentialsBytes = Convert.FromBase64String(authHeader.Parameter);
                var credentials = Encoding.UTF8.GetString(credentialsBytes).Split(':');
                var username = credentials[0];
                var password = credentials[1];
                var checkUser = _configuration["Auth:User"];
                var checkPwd  = _configuration["Auth:Pwd"];
                if (username != checkUser || password != checkPwd)
                {
                    authResult = AuthenticateResult.Fail("Invalid Username or Password");
                    return await Task.FromResult(authResult);
                }
                var claims = new[] {
                    new Claim(ClaimTypes.NameIdentifier, username),
                    new Claim(ClaimTypes.Name, username),
                };
                var identity = new ClaimsIdentity(claims, Scheme.Name);
                var principal = new ClaimsPrincipal(identity);
                var ticket = new AuthenticationTicket(principal, Scheme.Name);
                authResult = AuthenticateResult.Success(ticket);
                return await Task.FromResult(authResult);
            }
            catch (Exception ex)
            {
                authResult = AuthenticateResult.Fail("Invalid Authorization Header");
                return await Task.FromResult(authResult);
            }
        }

        /// <summary>
        /// 自定义面板认证-变动
        /// </summary>
        protected override Task HandleChallengeAsync(AuthenticationProperties properties)
        {
            Response.StatusCode = 401;
            Response.Headers["WWW-Authenticate"] = $"Basic realm=\"{Scheme.Name}\", charset=\"UTF-8\"";
            return base.HandleChallengeAsync(properties);
        }
    }
}

然后创建一个异常处理中间件里面,里面专门针对cap面板的路径做处理:

using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Extensions;

namespace Middleware
{
    /// <summary>
    /// 全局异常处理 中间件
    /// </summary>
    public class ExceptionHandlerMiddleware
    {
        private readonly RequestDelegate next;
        private readonly ILogger logger;

        /// <summary>
        /// 全局异常处理 中间件
        /// </summary>
        public ExceptionHandlerMiddleware(RequestDelegate next, ILoggerFactory loggerFactory)
        {
            this.next = next;
            logger = loggerFactory.CreateLogger("globeException");
        }

        /// <summary>
        /// 全局异常处理 中间件
        /// </summary>
        public async Task Invoke(HttpContext context)
        {
            try
            {
                //CAP面板认证
                if (context.Request.Path.ToString().ToLower().Contains("/cap/"))
                {
                    var authResult = await context.AuthenticateAsync(MyAuthDefaults.Scheme);
                    if (authResult.Succeeded == false) //认证失败
                    {
                        await context.ChallengeAsync(MyAuthDefaults.Scheme);
                        return;
                    }
                    context.User = authResult.Principal;  //成功后用户信息返回
                    await next(context);  // 如果需要继续处理请求,可以调用下一个中间件
                    return;
                }
                else
                {
                    await next(context);
                }
            }
            catch (Exception ex)
            {
                string exStr = ex.Message;
            }
        }

    }
}

添加针对面板的扩展:

using Extensions;

namespace Extensions
{
    public static class CapDashboardExtensions
    {
        /// <summary>
        /// 带认证的面板
        /// </summary>
        public static void AddCapDashboard(this IServiceCollection services, WebApplicationBuilder builder)
        {
            services.AddAuthentication(MyAuthDefaults.Scheme)
            .AddScheme<MyAuthSchemeOptions, MyAuthHandler>(MyAuthDefaults.Scheme, options => { });

            services.AddAuthorization(options =>
            {
                options.AddPolicy(MyAuthDefaults.Policy, policy =>
                {
                    policy.AddAuthenticationSchemes(MyAuthDefaults.Scheme).RequireAuthenticatedUser();
                });
            });
            services.AddCors(options =>
            {
                options.AddDefaultPolicy(builder =>
                {
                    builder.AllowCredentials().AllowAnyHeader().AllowAnyMethod();
                });
            });
            services.AddCap(options =>
            {
                options.FailedRetryInterval = 60;
                options.FailedRetryCount = 50;
                //需要认证
                options.UseDashboard(d =>
                {
                    d.PathMatch = "/Cap";
                    d.UseChallengeOnAuth = true;
                    d.DefaultChallengeScheme = MyAuthDefaults.Scheme;
                });
            });
        }
    }
}

最后在Program里面加上面板的扩展:


var builder = WebApplication.CreateBuilder(args);
var services = builder.Services;
services.AddCapDashboard(builder); //添加Cap面板

var app = builder.Build();
app.UseCors();
app.UseAuthentication(); // 启用认证中间件
app.UseAuthorization();

然后项目启动后,域名加上cap:

http://127.0.0.1:80/cap

就可以进入认证界面了:

大功搞成!输入代码里的账号,就可以进入CAP面板了:

因为网上找不到简单的CAP认证实现功能,所以这个功能摸索着开发了好久。

创作不易,给点鼓励,谢谢!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/703862.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Apache HttpClient总览

一、重大版本 Apache HttpClient 4.x 系列 • HttpClient 4.0&#xff08;发布于2008年左右&#xff09;&#xff1a;这是一个重要的里程碑&#xff0c;标志着HttpClient从Jakarta Commons项目转移到Apache HttpComponents项目。4.0版进行了大量的重构&#xff0c;引入了新…

谷歌利用人工智能来推动搜索,显示出其组织信息的方式存在问题

谷歌利用人工智能来推动搜索&#xff0c;显示出其组织信息的方式存在问题 从相关文件到新闻报道、商业、音乐和社会互动&#xff0c;世界上的大部分信息现在都在网上。谷歌成立于1998年&#xff0c;其使命是“组织世界上的信息&#xff0c;使其普遍可用和有用”&#xff0c;它…

STM32理论 —— μCOS-Ⅲ(2/2):时间管理、消息队列、信号量、任务内嵌信号量/队列

文章目录 9. 时间管理9.1 OSTimeDly()9.2 OSTimeDlyHMSM()9.3 OSTimeDlyResume()9.4 延时函数实验 10. 消息队列10.1 创建消息队列函数OSQCreate()10.2 发送消息到消息队列函数(写入队列)OSQPost()10.3 获取消息队列中的消息函数(读出队列)OSQPend()10.4 消息队列操作实验 11. …

深度学习500问——Chapter11:迁移学习(2)

文章目录 11.2 迁移学习的基本思路有哪些 11.2.1 基于样本迁移 11.2.2 基于特征迁移 11.2.3 基于模型迁移 11.2.4 基于关系迁移 11.2 迁移学习的基本思路有哪些 迁移学习的基本方法可以分为四种。这四种基本方法分别是&#xff1a;基于样本的迁移&#xff0c;基于模型的迁移&a…

【高阶数据结构】红黑树详解

目录 前言一、红黑树的概念二、红黑树的性质三、红黑树节点的定义四、红黑树的插入情况1&#xff1a;cur为红&#xff0c;parent为红&#xff0c;grandfather为黑&#xff0c;uncle为红情况2&#xff1a; cur为红&#xff0c;parent为红&#xff0c;grandfather为黑&#xff0c…

GD32C103/GD32C113 CANFD

CANFD介绍 FD全称是 Flexible Data-Rate,顾名思义&#xff0c;表示CAN-FD 的帧报文具有数据场波特率可变的特性&#xff0c;即仲裁场合数据控制场使用标准的通信波特率&#xff0c;而到数据场就会切换为更高的通信波特率&#xff0c;车端常用的为2Mbit/s和5Mbit/s,从而达到提高…

harbor问题总结

1. http协议的仓库docker login不上&#xff0c;更改/etc/docker/daemon.json&#xff0c;加一个镜像仓库地址 http: server gave HTTP response to HTTPS client 分析一下这个问题如何解决中文告诉我详细的解决方案-CSDN博客 2. Error response from daemon: login attempt t…

机器学习笔记 - 用于3D数据分类、分割的Point Net的网络实现

上一篇,我们大致了解了Point Net的原理,这里我们要进行一下实现。 机器学习笔记 - 用于3D数据分类、分割的Point Net简述-CSDN博客文章浏览阅读3次。在本文中,我们将了解Point Net,目前,处理图像数据的方法有很多。从传统的计算机视觉方法到使用卷积神经网络到Transforme…

【spring 】支持spring WebFlux 的容器

spring WebFlux 是 Spring 5 引入的响应式 Web 框架&#xff0c;它支持非阻塞、事件驱动的编程模型&#xff0c;特别适合处理高并发的场景。 Spring WebFlux 可以运行在多种容器上 包括下面&#xff1a; Netty: Netty 是一个异步事件驱动的网络应用程序框架&#xff0c;用于快…

WPF/C#:程序关闭的三种模式

ShutdownMode枚举类型介绍 ShutdownMode是一个枚举类型&#xff0c;它定义了WPF应用程序的关闭方式。这个枚举类型有三个成员&#xff1a; OnLastWindowClose&#xff1a;当最后一个窗口关闭或者调用System.Windows.Application.Shutdown方法时&#xff0c;应用程序会关闭。O…

分布式物联网平台特点

随着物联网&#xff08;IoT&#xff09;技术的飞速发展&#xff0c;我们正步入一个万物互联的新时代。在这个时代&#xff0c;设备、数据和服务的无缝集成是实现智能化的关键。分布式物联网平台作为这一进程的核心&#xff0c;正在成为构建智能世界的基石。 一、分布式物联网平…

【培训】企业档案管理专题(私货)

导读&#xff1a;通过该专题培训&#xff0c;可以系统了解企业档案管理是什么、为什么、怎么做。尤其是对档案的价值认知&#xff0c;如何构建与新质生产力发展相适应的企业档案工作体系将有力支撑企业新质生产力的发展&#xff0c;为企业高质量发展贡献档案力量&#xff0c;提…

IDEA创建简单web(servlet)项目(server为tomcat)

引言 鉴于网上很少有关于IDEA开发servlet项目的教程&#xff08;24版idea&#xff0c;并且servlet技术十分复古&#xff0c;很少有人用到&#xff0c;能够理解&#xff0c;该文章旨在为在校的学生提供一个参考&#xff0c;项目技术简单&#xff09;本人在此总结从头开始到项目…

C数据结构:排序

目录 冒泡排序 选择排序 堆排序 插入排序 希尔排序 快速排序 hoare版本 挖坑法 前后指针法 快速排序优化 三数取中法 小区间优化 快速排序非递归 栈版本 队列版本 归并排序 归并排序非递归 ​编辑 计数排序 各排序时间、空间、稳定汇总 冒泡排序 void Bub…

学习grdecl文件格式之后的事情

学习了grdecl文件格式&#xff0c;搞地质的专业人士都知道&#xff0c;这是专门用在地质上的油藏软件&#xff08;个人感觉就是斯伦贝谢的Petrel的&#xff09;的一种文件格式&#xff0c;正好自己也在学习三维的开发&#xff0c;顺手写了一个简单的读取grdecl算法&#xff0c;…

[深度学习]使用python转换pt并部署yolov10的tensorrt模型封装成类几句完成目标检测加速任务

【简单介绍】 使用Python将YOLOv10模型从PyTorch格式&#xff08;.pt&#xff09;转换为TensorRT格式&#xff0c;并通过封装成类来实现目标检测加速任务&#xff0c;是一个高效且实用的流程。以下是该过程的简要介绍&#xff1a; 模型转换&#xff1a; 利用官方提供导出命令…

Roboflow 图片分类打标

今天准备找个图片标注工具&#xff0c;在网上搜了一下&#xff0c;看 Yolo 的视频中都是用 Roboflow 工具去尝试了一下&#xff0c;标注确实挺好用的&#xff0c;可以先用一些图片训练一个模型&#xff0c;随后用模型进行智能标注。我主要是做标注然后到处到本地进行模型的训练…

html是什么?http是什么?

html Html是什么&#xff1f;http是什么&#xff1f; Html 超文本标记语言&#xff1b;负责网页的架构&#xff1b; http(&#xff08;HyperText Transfer Protocol&#xff09;超文本传输协议&#xff1b; https&#xff08;全称&#xff1a;Hypertext Transfer Protocol …

Linux 基本指令2

cp 指令 cp[选项]源文件 目标文件 将源文件的内容复制到目标文件中&#xff0c;源文件可以有多个&#xff0c;最后一个文件为目标文件&#xff0c;目标文件也可以是一段路径&#xff0c;若目的地不是一个目录的话会拷贝失败。若没有路径上的目录则会新建一个&#xff0c;若源是…

.NET MAUI Sqlite数据库操作(一)

一、安装 NuGet 包 安装 sqlite-net-pcl 安装 SQLitePCLRawEx.bundle_green 二、配置数据库&#xff08;数据库文件名和路径&#xff09; namespace TodoSQLite; public static class Constants {public const string DatabaseFilename "TodoSQLite.db3";//数据库…