Spring Boot应用集成Actuator端点自定义Filter解决未授权访问的漏洞

一、前言

我们知道想要实时监控我们的应用程序的运行状态,比如实时显示一些指标数据,观察每时每刻访问的流量,或者是我们数据库的访问状态等等,需要使用到Actuator组件,但是Actuator有一个访问未授权问题,简单说就是其他人可以通过Actuator组件暴露的URL进行端点信息访问,甚至shutdown应用。那么我们有没有什么解决方法呢?

二、解决方案(Actuator端口与应用端口一致)

我们创建一个spring Boot项目进行演示说明。思路就是对spring Boot actuator暴露的URL访问时,增加携带用户名、密码,同时增加一个Filter进行拦截,为了防止密码泄露,需要对密码进行加密配置,由于后端需要进行对比密码,所以我们需要采用对称加密,这里我们采用SM4加密算法,可以参考博文:

使用SM4国密加密算法对Spring Boot项目数据库连接信息以及yaml文件配置属性进行加密配置(读取时自动解密)

为什么不采用主流的集成Spring Security组件呢,出于两方面考虑:

  • 集成Spring Security相对Filter来说比较重量级
  • 集成Spring Security进行Actuator端点认证可能会与原有业务安全认证冲突

2.1 创建spring Boot项目,导入相关依赖

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency><groupId>org.bouncycastle</groupId><artifactId>bcprov-jdk15to18</artifactId><version>1.76</version>
</dependency><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.25</version>
</dependency>

2.2 增加相关配置

management:endpoints:web:exposure:include: health

2.3 启动验证

在这里插入图片描述

2.4 授权改造

2.4.1 增加自定义配置
management:endpoints:web:exposure:include: healthwhiteUrl:   # 白名单,配置白名单的URL请求时不需要验证,多个可以用英文逗号分隔user: admin # 认证用户password: '@SM4@-HUYu9S6osKi65pZr7YQO9w=='  # 认证用户密码 SM4Utils.encryptStr("password")
2.4.2 自定义授权过滤器逻辑
package com.learn.filter;import com.fasterxml.jackson.databind.ObjectMapper;
import com.learn.SM4Utils;
import org.springframework.http.MediaType;
import org.springframework.util.StringUtils;
import sun.misc.BASE64Decoder;import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;public class ActuatorFilter implements Filter {private String[] whiteUrl;private String user;private String password;public ActuatorFilter(String whiteUrl, String user, String password) {this.whiteUrl = whiteUrl == null ? null : whiteUrl.split(",");this.user = user;this.password = password;}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {boolean flag = false;HttpServletRequest httpServletRequest = ((HttpServletRequest) servletRequest);String url = httpServletRequest.getRequestURI();// 判断是否是白名单if (whiteUrl != null && whiteUrl.length != 0) {for (String str : whiteUrl) {if (url.equals(str)) {flag = true;break;}}}if (flag) { // 如果是白名单则直接放行filterChain.doFilter(servletRequest, servletResponse);} else {if (StringUtils.hasText(user) && StringUtils.hasText(password)) { // 如果配置用户名密码String authorization = httpServletRequest.getHeader("authorization");if (StringUtils.hasText(authorization)) { // Basic YWRtaW46YWRtaW4=user = SM4Utils.decryptStr(user);password = SM4Utils.decryptStr(password); // @Value注入可以自动解密,这里手动解密String auth = authorization.replace("Basic ", "");auth = new String(new BASE64Decoder().decodeBuffer(auth), StandardCharsets.UTF_8);String[] authArr = auth.split(":");if (user.equals(authArr[0]) && password.equals(authArr[1])) { //如果用户名密码都可以匹配上则进行放行filterChain.doFilter(servletRequest, servletResponse);} else {errorHandler((HttpServletResponse) servletResponse, "user or password info error!");return;}} else {errorHandler((HttpServletResponse) servletResponse, "Authorization info must be not null");return;}} else { // 如果没有配置用户名密码则直接放行filterChain.doFilter(servletRequest, servletResponse);}}}/*** 异常处理** @param response* @param msg* @throws IOException*/private void errorHandler(HttpServletResponse response, String msg) throws IOException {response.setContentType(MediaType.APPLICATION_JSON_VALUE);response.setCharacterEncoding(StandardCharsets.UTF_8.name());response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);ObjectMapper objectMapper = new ObjectMapper();PrintWriter writer = response.getWriter();writer.write(objectMapper.writeValueAsString(msg));writer.close();}
}
2.4.3 注册Filter生效
import com.learn.filter.ActuatorFilter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class ActuatorConfig {@Value("${management.endpoints.web.exposure.whiteUrl:}")private String whiteUrl;@Value("${management.endpoints.user:}")private String user;@Value("${management.endpoints.password:}")private String password;@Beanpublic FilterRegistrationBean filterRegistrationBean() {FilterRegistrationBean<ActuatorFilter> filterRegistrationBean = new FilterRegistrationBean<>();filterRegistrationBean.setFilter(new ActuatorFilter(whiteUrl, user, password));filterRegistrationBean.addUrlPatterns("/actuator/*");filterRegistrationBean.setName("ActuatorFilter");return filterRegistrationBean;}}

2.5 功能测试

  • 成功
    注意:URL格式: http://明文user:明文password@ip:port/actuator/health
    前端访问时需要携带密码,后端配置密码进行了加密,防止密码泄露。
    在这里插入图片描述

  • 失败

在这里插入图片描述

此时访问actuator端点都需要携带用户名密码了。

三、解决方案(Actuator端口与应用端口不一致)

众所周知,spring Boot Actuator组件的端口是可以自定义配置的,如果是自定义配置端口,那么上面的Filter不会生效,那么要怎么处理呢。

测试密码错误,此时还是会正常返回:

在这里插入图片描述

3.1 增加自定义端口配置

server:port: 8080management:server:port: 9999endpoints:web:exposure:include: healthwhiteUrl:   # 白名单,配置白名单的URL请求时不需要验证,多个可以用英文逗号分隔user: admin # 认证用户password: '@SM4@-HUYu9S6osKi65pZr7YQO9w=='  # 认证用户密码 SM4Utils.encryptStr("password")

3.2 修改Filter逻辑,增加Actuator端口判断

import com.fasterxml.jackson.databind.ObjectMapper;
import com.learn.SM4Utils;
import org.springframework.http.MediaType;
import org.springframework.util.StringUtils;
import sun.misc.BASE64Decoder;import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;public class ActuatorFilter implements Filter {private String[] whiteUrl;private String user;private String password;/*** actuator端口*/private Integer actuatorPort;public ActuatorFilter(String whiteUrl, String user, String password, Integer actuatorPort) {this.whiteUrl = whiteUrl == null ? null : whiteUrl.split(",");this.user = user;this.password = password;this.actuatorPort = actuatorPort;}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {HttpServletRequest httpServletRequest = ((HttpServletRequest) servletRequest);int serverPort = httpServletRequest.getServerPort();if (actuatorPort != null && serverPort == actuatorPort) { // 判断是否是actuator端口boolean flag = false;String url = httpServletRequest.getRequestURI();// 判断是否是白名单if (whiteUrl != null && whiteUrl.length != 0) {for (String str : whiteUrl) {if (url.equals(str)) {flag = true;break;}}}if (flag) { // 如果是白名单则直接放行filterChain.doFilter(servletRequest, servletResponse);} else {if (StringUtils.hasText(user) && StringUtils.hasText(password)) { // 如果配置用户名密码String authorization = httpServletRequest.getHeader("authorization");if (StringUtils.hasText(authorization)) { // Basic YWRtaW46YWRtaW4=user = SM4Utils.decryptStr(user);password = SM4Utils.decryptStr(password); // @Value注入可以自动解密,这里手动解密String auth = authorization.replace("Basic ", "");auth = new String(new BASE64Decoder().decodeBuffer(auth), StandardCharsets.UTF_8);String[] authArr = auth.split(":");if (user.equals(authArr[0]) && password.equals(authArr[1])) { //如果用户名密码都可以匹配上则进行放行filterChain.doFilter(servletRequest, servletResponse);} else {errorHandler((HttpServletResponse) servletResponse, "user or password info error!");return;}} else {errorHandler((HttpServletResponse) servletResponse, "Authorization info must be not null");return;}} else { // 如果没有配置用户名密码则直接放行filterChain.doFilter(servletRequest, servletResponse);}}} else {filterChain.doFilter(servletRequest, servletResponse);}}/*** 异常处理** @param response* @param msg* @throws IOException*/private void errorHandler(HttpServletResponse response, String msg) throws IOException {response.setContentType(MediaType.APPLICATION_JSON_VALUE);response.setCharacterEncoding(StandardCharsets.UTF_8.name());response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);ObjectMapper objectMapper = new ObjectMapper();PrintWriter writer = response.getWriter();writer.write(objectMapper.writeValueAsString(msg));writer.close();}
}

3.3 修改Filter注册逻辑

import com.learn.filter.ActuatorFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;@Configuration
public class ActuatorConfig {@Beanpublic ActuatorFilter actuatorFilter(Environment environment) {String whiteUrl = environment.getProperty("management.endpoints.web.exposure.whiteUrl");String user = environment.getProperty("management.endpoints.user");String password = environment.getProperty("management.endpoints.password");String portStr = environment.getProperty("management.server.port");Integer port = portStr == null ? null : Integer.parseInt(portStr);return new ActuatorFilter(whiteUrl, user, password, port);}}

3.4 注册ManagementContextConfiguration

这是最重要的一步,需要配置ManagementContextConfiguration,不然Filter依旧不会生效:

resource目录下新建META-INF目录,新建spring.factories文件

在这里插入图片描述

增加内容:

org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration=\com.learn.config.ActuatorConfig

3.5 测试

在这里插入图片描述

此时密码错误情况下就进行拦截了

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

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

2.21日学习打卡----初学Nginx(一)

2.21日学习打卡 目录: 2.21日学习打卡一. Nginx是什么&#xff1f;概述Nginx 五大应用场景HTTP服务器正向代理反向代理正向代理与反向代理的区别&#xff1a;负载均衡动静分离 为啥使用Nginx? 二.下载Nginx&#xff08;linux&#xff09;环境准备下载Nginx和安装NginxNginx源码…

自定义Chrome的浏览器开发者工具DevTools界面的字体和样式

Chrome浏览器开发者工具默认的字体太小&#xff0c;想要修改但没有相关设置。 外观——字体可以自定义字体&#xff0c;但大小不可以调整。 github上有人给出了方法 整理为中文教程&#xff1a; 1.打开浏览器开发者工具&#xff0c;点开设置——实验&#xff0c;勾上红框设…

Go语言中的流程控制

「万事开头难&#xff0c;视频号500粉直播需要你的助力&#xff01;你的支持是我前进的动力&#xff01;」 1、Golang 中的流程控制 流程控制是每种编程语言控制逻辑走向和执行次序的重要部分&#xff0c;流程控制可以说是一门语言的“经脉”。Go 语言中最常用的流程控制有 if …

一文读懂Linux内核中的Device mapper映射机制

一、 简介 本文总结Device mapper的映射机制。Device mapper是Linux2.6内核中提供的一种逻辑设备到物理设备的映射框架机制&#xff0c;在该机制下&#xff0c;用户可以很方便的根据自己的需要指定实现存储资源的管理策略&#xff0c;当前比较流行的Linux的逻辑卷管理器比如&a…

程序与算法

数据结构是数据组织、存储和运算的总和。在计算机处理的大量数据中,数据结构和算法是相互关联、彼此联系的。对实际问题选择了一种好的数据结构之后,还得有一个好的算法,才可以更好地求解问题。一个算法应该具备以下特征:1. 有穷性;2. 确定性;3. 可行性;4. 输入;5. 输出…

一图揭秘为什么开发者都选择华为云软件开发生产线CodeArts

华为云软件开发生产线CodeArts是一站式、全流程、安全可信的云原生DevSecOps云平台&#xff0c;集华为30年研发实践、前沿研发理念、先进研发工具为一体&#xff0c;覆盖需求、开发、测试、部署等软件交付全生命周期环节&#xff0c;为开发者打造全云化研发体验。 体验通道&am…

如何快速卸载windows电脑的一些软件?

本系列是一些电脑常规操作的普及&#xff0c;有需要借鉴即可 注&#xff1a;每个电脑都会有差异&#xff0c;参考即可。 其实大部分软件你删除桌面上的图标不等于删除&#xff0c;因为桌面上的那个图标就是一个简单的快捷方式而已。 在这里插入图片描述 那如何正确的卸载软件呢…

基于Python3的数据结构与算法 - 04 快速排序

一、快速排序思路 快速排序特点&#xff1a;快 步骤&#xff1a; 取一个元素p&#xff08;第一个元素&#xff09;&#xff0c;使元素p归为&#xff1b;列表被p分成两部分&#xff0c;左边都比p小&#xff0c;右边都比p大&#xff1b;递归完成排序。 因此我们可以得到快速排…

java 面向对象-上:类的结构之二

类的设计中&#xff0c;两个重要结构之二&#xff1a;方法 方法 描述类应该具的功能。 比如&#xff1a;Math类&#xff1a;sqrt()\random() \... Scanner类&#xff1a;nextXxx() ... Arrays类&#xff1a;sort() \ binarySearch() \ toString() \ equals() \ ... 1.举例 p…

H.323

1 H.323 信令标准。 是 ITU-T 于 1996 年制定的为在局域网上传送话音信息的建议书。 1998 年的第二个版本改用的名称是“基于分组的多媒体通信系统”。 H.323 是互联网的端系统之间进行实时声音和视频会议的标准。 H.323 不是一个单独的协议&#xff0c;而是一组协议。包括…

TYPE-C接口桌面显示器:视频与充电的双重革新

在现代科技的浪潮中&#xff0c;TYPE-C接口桌面显示器崭露头角&#xff0c;它不仅仅是一台显示器&#xff0c;更是充电与视频传输的完美融合。这种新型的显示器&#xff0c;凭借其TYPE-C接口&#xff0c;实现了从DC电源到PD协议充电的华丽转身&#xff0c;为众多设备如笔记本电…

【学网攻】 第(30)节 -- 综合实验三

系列文章目录 目录 系列文章目录 文章目录 前言 一、综合实验 二、实验 1.引入 实验目标 实验设备 实验拓扑图 实验配置 文章目录 【学网攻】 第(1)节 -- 认识网络【学网攻】 第(2)节 -- 交换机认识及使用【学网攻】 第(3)节 -- 交换机配置聚合端口【学网攻】 第(4)节…

beego代理前端web的bug

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 一、beego代理前端web的bug总结 一、beego代理前端web的bug *报错&#xff0c;为web压缩包index.html里面的注释被错误解析&#xff0c;删掉就行 2024/02/22 10:2…

如何在Pycharm中导入第三方库(以pyecharts为例子)

打开Pycharm 点击右上角文件->设置->项目->pythonProject&#xff08;Python解释器&#xff09; 点击下图号 下一步&#xff1a;在搜索栏中直接搜索第三方包pyecharts并安装即可 以上便为使用Pycharm安装第三方库的全过程。 温馨小提示&#xff0c;如果大家在Pychar…

报表开发工具DevExpress .NET Reporting v23.2亮点 - 支持智能标签

DevExpress Reporting是.NET Framework下功能完善的报表平台&#xff0c;它附带了易于使用的Visual Studio报表设计器和丰富的报表控件集&#xff0c;包括数据透视表、图表&#xff0c;因此您可以构建无与伦比、信息清晰的报表。 DevExpress Reporting控件日前正式发布了v23.2…

数字化转型导师坚鹏:县域数字化转型案例研究

县域数字化转型案例研究 课程背景&#xff1a; 很多县级政府存在以下问题&#xff1a; 不清楚县域数字化转型的发展模式 不清楚县域数字化转型的成功案例 课程特色&#xff1a; 针对性强 实用性强 创新性强 学员收获: 学习县域数字化转型的发展模式。 学习县…

JavaSec 之 XXE 简单了解

文章目录 XMLReaderSAXReaderSAXBuilderDocumentBuilderUnmarshaller**SAXParserFactory**XMLReaderFactoryDigester总结 XMLReader public String XMLReader(RequestBody String content) {try {XMLReader xmlReader XMLReaderFactory.createXMLReader();// 修复&#xff1a…

链表之“无头单向非循环链表”

目录 ​编辑 1.顺序表的问题及思考 2.链表 2.1链表的概念及结构 2.2无头单向非循环链表的实现 1.创建结构体 2.单链表打印 3.动态申请一个节点 3.单链表尾插 4.单链表头插 5.单链表尾删 6.单链表头删 7.单链表查找 8.单链表在pos位置之前插入x 9.单链表删除pos位…

C++的stack容器->基本概念、常见接口

#include<iostream> using namespace std; #include <stack> //栈stack容器常用接口 void test01() { //创建栈容器 栈容器必须符合先进后出 stack<int> s; //向栈中添加元素&#xff0c;叫做 压栈 入栈 s.push(10); s.push(20); s…

华清远见作业第四十一天——Qt(第三天)

思维导图&#xff1a; 编程 完善对话框&#xff0c;点击登录对话框&#xff0c;如果账号和密码匹配&#xff0c;则弹出信息对话框&#xff0c;给出提示”登录成功“&#xff0c;提供一个Ok按钮&#xff0c;用户点击Ok后&#xff0c;关闭登录界面&#xff0c;跳转到其他界面 如…