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://www.mzph.cn/news/698452.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Postgresql源码(123)事务提交时三段资源释放分析ResourceOwnerRelease

0 总结 三段释放原因&#xff1a;因为如果先释放锁&#xff0c;没有释放一些共享资源&#xff08;比如pin住的buffer&#xff09;&#xff0c;别人拿到锁后发现我们仍然持有一些资源&#xff0c;就会有问题。所以三阶段释放主要是以锁为分界线&#xff0c;先释放锁保护的资源&…

智慧未来:人工智能驱动下的创新与发展

智慧未来&#xff1a;人工智能驱动下的创新与发展 随着人工智能技术的迅猛发展和广泛应用&#xff0c;我们正迎来一个智慧未来的时代。人工智能作为驱动力推动着创新与发展&#xff0c;改变着我们的生活、工作和社会。让我们一起探讨人工智能驱动下的创新与发展所带来的影响和…

酷开科技丨新年新玩法!酷开系统壁纸模式给客厅“换”新

甲辰龙年即将到来&#xff0c;新年新家新气象&#xff0c;快到酷开系统壁纸模式中挑选一款喜欢的壁纸&#xff0c;为新的一年增添一份美好和喜悦吧&#xff01; 酷开科技将更多的电视新玩法带给你&#xff0c;让你的电视成为家庭中的焦点&#xff01;酷开系统壁纸模式&#xf…

LabVIEW高效核磁测井仪器多线程优化

LabVIEW高效核磁测井仪器多线程优化 为提高核磁测井仪器的测试效率与性能&#xff0c;开发了基于LabVIEW的多线程优化模型。该研究针对传统的核磁测井仪器软件&#xff0c;在多任务调度测试和并行技术需求上存在的效率不高和资源利用率低的问题&#xff0c;提出了一个多线程优…

智能家居现状分析及未来展望

当前现状 家居行业经过多年发展&#xff0c;顺利完成了从无到有的进化历程&#xff0c;现正在智能化的道路上奋力驰骋&#xff0c;虽发展迅速但也面临一些问题。主要有&#xff1a; APP操作复杂、UI不统一 传统硬件厂家的优势在设备制造领域&#xff0c;让设备“上网”不是其…

SQL注入工具之SQLmap入门操作

了解SQLmap 基础操作 SQLmap是一款自动化的SQL注入工具&#xff0c;可以用于检测和利用SQL注入漏洞。 以下是SQLmap的入门操作步骤&#xff1a; 1.下载SQLmap&#xff1a;可以从官方网站&#xff08;https://sqlmap.org/&#xff09;下载最新版本的SQLmap。 2.打开终端&#…

修改单据转换规则后保存报错提示

文章目录 修改单据转换规则后保存报错提示 修改单据转换规则后保存报错提示

Neo4j导入数据之JAVA JDBC

目录结构 前言设置neo4j外部访问代码整理maven 依赖java 代码 参考链接 前言 公司需要获取neo4j数据库内容进行数据筛查&#xff0c;neo4j数据库咱也是头一次基础&#xff0c;辛辛苦苦安装好整理了安装neo4j的步骤&#xff0c;如今又遇到数据不知道怎么创建&#xff0c;关关难…

VegaPrime 2013 VP2013

Vega Prime 2013 VegaPrime 2013 VP2013

基于springboot+vue实现的大学竞赛报名管理系统

一、系统架构 前端&#xff1a;vue2 | echarts 后端&#xff1a;springboot | mybatis 环境&#xff1a;jdk1.8 | mysql | maven 二、代码及数据库 三、功能介绍 01. 登录页 02. 教师端-统计分析 03. 教师端-竞赛通知管理 04. 教师端-获奖通告管理 05. 教师端…

学生成绩管理系统(C语言课设 )

这个学生成绩管理系统使用C语言编写&#xff0c;具有多项功能以方便管理学生信息和成绩。首先从文件中读取数据到系统中&#xff0c;并提供了多种功能&#xff08;增删改查等&#xff09;选项以满足不同的需求。 学生成绩管理系统功能: 显示学生信息增加学生信息删除学生信息…

《论文阅读》通过识别对话中的情绪原因来提高共情回复的产生 EMNLP 2021

《论文阅读》通过识别对话中的情绪原因来提高共情回复的产生 EMNLP 2021 前言简介方法实现Emotion ReasonerResponse Generator实验结果示例总结前言 亲身阅读感受分享,细节画图解释,再也不用担心看不懂论文啦~ 无抄袭,无复制,纯手工敲击键盘~ 今天为大家带来的是《Improv…

chatGPT 使用随想

一年前 chatGPT 刚出的时候&#xff0c;我就火速注册试用了。 因为自己就是 AI 行业的&#xff0c;所以想看看国际上最牛的 AI 到底发展到什么程度了. 自从一年前 chatGPT 火出圈之后&#xff0c;国际上的 AI 就一直被 OpenAI 这家公司引领潮流&#xff0c;一直到现在&#x…

《VitePress 简易速速上手小册》第9章 VitePress 的扩展与插件(2024 最新版)

文章目录 9.1 插件生态系统概述9.1.1 基础知识点解析9.1.2 重点案例:SEO 优化插件9.1.3 拓展案例 1:社交分享插件9.1.4 拓展案例 2:内容搜索插件9.2 常用插件介绍与应用9.2.1 基础知识点解析9.2.2 重点案例:使用 SEO 插件9.2.3 拓展案例 1:集成社交分享功能9.2.4 拓展案例…

24-2-22学习总结

练习题 P8637 [蓝桥杯 2016 省 B] 交换瓶子 # [蓝桥杯 2016 省 B] 交换瓶子 ## 题目描述 有 $N$ 个瓶子&#xff0c;编号 $1 \sim N$&#xff0c;放在架子上。 比如有 $5$ 个瓶子&#xff1a; $$2,1,3,5,4$$ 要求每次拿起 $2$ 个瓶子&#xff0c;交换它们的位置。 经过…

RisingWave最佳实践-利用Dynamic filters 和 Temporal filters 实现监控告警

心得的体会 刚过了年刚开工&#xff0c;闲暇之余调研了分布式SQL流处理数据库–RisingWave&#xff0c;本人是Flink&#xff08;包括FlinkSQL和Flink DataStream API&#xff09;的资深用户&#xff0c;但接触到RisingWave令我眼前一亮&#xff0c;并且拿我们生产上的监控告警…

sql server想要小数点后向下取整怎么搞

select FORMAT(3.169, N2) as 四舍五入1, CAST(3.169 AS decimal(9,2)) as 四舍五入2, ROUND(3.169, 2) as 四舍五入3, CAST(FLOOR(3.169 * 100) / 100 AS decimal(9,2)) as 向下取整1, FLOOR(3.169 * 100) / 100 as 向下取整2, ceiling(3.169 * 100) / 100 as 向上取整—…

python 几种常见的音频数据读取、保存方式

1. soundfile 库的使用 soundfile库是一个Python库&#xff0c;主要用于读取和写入音频文件。它支持多种音频格式&#xff0c;包括WAV、AIFF、FLAC和OGG等。通过soundfile库&#xff0c;用户可以方便地将numpy数组存储到音频文件或者将音频文件加载到numpy数组中。此外&#x…

AI 视频 | Stable Video 开放公测了,免部署,免费使用!谁说 4 秒的 AI 视频不香?!

谁说 4 秒的视频不香&#xff1f;2.21 日&#xff0c;Stable Video 开放公测了&#xff0c;不需要自己部署了&#xff0c;直接在网页上就可以生成视频了。 下面这些视频&#xff0c;都是通过 Stable Video Diffusion 生成的&#xff0c;可以先来感受一下&#xff1a; Stable V…

IPsec、安全关联、网络层安全协议

网络层安全协议 IP 几乎不具备任何安全性&#xff0c;不能保证&#xff1a; 1.数据机密性 2.数据完整性 3.数据来源认证 由于其在设计和实现上存在安全漏洞&#xff0c;使各种攻击有机可乘。例如&#xff1a;攻击者很容易构造一个包含虚假地址的 IP 数据报。 IPsec 提供了标…