目标

不暴露Druid内置的servlet到公网(防止被爆破、防止Druid出现 0 Day漏洞后被直接波及)。拦截请求,使用自定义鉴权机制,再放行请求。

版本信息

  • Java 17
  • SpringBoot 2.7.3
  • druid-spring-boot-starter 1.2.12
  • Apache Tika 2.4.1

application.yml

spring:thymeleaf:cache: falsedatasource:username: xflpassword: amazingxfl666url: jdbc:mysql://localhost:3306/xfl_mybigdata?useUnicode=true&characterEncoding=utf8&characterSetResults=utf8&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=truedriver-class-name: com.mysql.cj.jdbc.Drivertype: com.alibaba.druid.pool.DruidDataSource# druid 其它配置druid:initialSize: 5minIdle: 5maxActive: 60maxWait: 120000defaultAutoCommit: truepoolPreparedStatements: truemaxPoolPreparedStatementPerConnectionSize: 50aop-patterns: cc.xfl12345.mybigdata.server.*use-global-data-source-stat: trueconnectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000;filters: config,stat,slf4j,wall # wall用于防火墙filter:wall:config:alter-table-allow: true# 允许一次执行多条语句multi-statement-allow: true# 允许非基本语句的其他语句none-base-statement-allow: true# 是否允许重置数据 (已设计成必填。安全相关,必须设置)stat-view-servlet:reset-enable: false#是否启用StatFilter默认值false,用于采集 web-jdbc 关联监控的数据。web-stat-filter:enabled: true#排除一些静态资源,以提高效率exclusions: "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*"#需要监控的 urlurl-pattern: "/*"session-stat-enable: true       # 开启session统计功能session-stat-max-count: 1000    # session的最大个数,默认100sql:init:encoding: UTF-8jackson:date-format: yyyy-MM-dd HH:mm:ss.SSSproperty-naming-strategy: LOWER_CAMEL_CASEtime-zone: GMT+8default-property-inclusion: non_nullservlet:multipart:max-file-size: -1mvc:converters:preferred-json-mapper: jacksonview:prefix: /WEB-INF/views/suffix: .jspcontentnegotiation:favor-parameter: truedata:rest:default-media-type: application/jsonserver:port: 8880tomcat:accesslog:enabled: trueencoding: UTF-8ipv6-canonical: trueremoteip:protocol-header: X-Forwarded-Protouse-relative-redirects: trueservlet:encoding:enabled: truecharset: UTF-8force-response: trueforward-headers-strategy: nativehttp2:enabled: truedebug: true

SpringBoot 配置 SpringMVC

package cc.xfl12345.mybigdata.server.mysql.spring.boot.conf;import cc.xfl12345.mybigdata.server.mysql.spring.web.controller.DruidStatController;
import cc.xfl12345.mybigdata.server.mysql.spring.web.interceptor.DruidStatInterceptor;
import lombok.Getter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configuration
@ComponentScan(basePackageClasses = DruidStatController.class)
public class DruidSpringMvcConfig implements WebMvcConfigurer {@Getterprotected DruidStatInterceptor druidStatInterceptor;@Autowiredpublic void setDruidStatInterceptor(DruidStatInterceptor druidStatInterceptor) {this.druidStatInterceptor = druidStatInterceptor;}@Overridepublic void addInterceptors(InterceptorRegistry registry) {// 注册 Druid 的路由拦截器registry.addInterceptor(druidStatInterceptor).addPathPatterns(String.format("/%s/**", DruidStatController.servletName));}
}

DruidStatInterceptor.java

package cc.xfl12345.mybigdata.server.mysql.spring.web.interceptor;import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;/*** 拦截 Druid 监视器请求。限制访问。*/
@Slf4j
public class DruidStatInterceptor implements HandlerInterceptor {/*** 拦截请求*/@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {//        System.out.println("拦截请求");return true;}/*** 拦截响应*/@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,ModelAndView modelAndView) throws Exception {//        System.out.println("拦截响应");}/*** 拦截渲染*/@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)throws Exception {//        System.out.println("拦截渲染");}
}

DruidStatController.java

package cc.xfl12345.mybigdata.server.mysql.spring.web.controller;import com.alibaba.druid.stat.DruidStatService;
import lombok.extern.slf4j.Slf4j;
import org.apache.tika.Tika;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.http.HttpMethod;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Writer;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;@RestController
@Slf4j
@RequestMapping(DruidStatController.servletName)
public class DruidStatController implements InitializingBean {protected DruidStatService statService = DruidStatService.getInstance();public static final String servletName = "druid";public static final String servletPathCache1 = "/" + servletName;// 为安全考虑,强制必须设置 reset-enable 的值@Value("${spring.datasource.druid.stat-view-servlet.reset-enable}")public void setResetEnable(boolean resetEnable) {statService.setResetEnable(resetEnable);}public boolean isResetEnable() {return statService.isResetEnable();}protected String resourceRootPath = "support/http/resources/";protected URL rootFileURL;protected String rootFileUrlString;protected ConcurrentHashMap<String, ResourceDetail> druidFrontendFiles = new ConcurrentHashMap<>();public static class ResourceDetail {public Resource resource;public String mimeType;public String path;}@Overridepublic void afterPropertiesSet() throws Exception {Tika tika = new Tika();ClassLoader classLoader = Thread.currentThread().getContextClassLoader();rootFileURL = Objects.requireNonNull(classLoader.getResource(resourceRootPath));rootFileUrlString = rootFileURL.toString();// String rootFileClasspathBase = rootFileUrlString.substring(0, rootFileUrlString.lastIndexOf(resourceRootPath));Resource[] resources = new PathMatchingResourcePatternResolver().getResources(resourceRootPath + "**");for (Resource resource : resources) {URL currentFileURL = resource.getURL();String relativePath;if (resource instanceof ClassPathResource classPathResource) {relativePath = '/' + classPathResource.getPath();} else {relativePath = '/' + currentFileURL.toString().substring(rootFileUrlString.length());}relativePath = relativePath.substring(resourceRootPath.length());int lastIndexOfSplitChar = relativePath.lastIndexOf('/');// 如果是文件,而不是文件夹if (relativePath.length() - 1 > lastIndexOfSplitChar) {String filename = relativePath.substring(lastIndexOfSplitChar + 1);ResourceDetail detail = new ResourceDetail();detail.resource = resource;detail.path = relativePath;try (InputStream inputStream = resource.getInputStream()) {detail.mimeType = tika.detect(inputStream, filename);}log.debug(relativePath + " <---> " + currentFileURL.toString());druidFrontendFiles.put(relativePath, detail);}}}@GetMapping(path = {"", "index"})public void redirectIndexPage(HttpServletResponse response) throws IOException {response.sendRedirect("./index.html");}@RequestMapping("/**")public void forward(HttpServletRequest request, HttpServletResponse response) throws IOException {String relativeURL = request.getServletPath().substring(servletPathCache1.length());ResourceDetail resourceDetail = druidFrontendFiles.get(relativeURL);// 如果命中了静态资源,则直接返回文件。if (resourceDetail != null) {response.setStatus(HttpServletResponse.SC_OK);response.setContentType(resourceDetail.mimeType);response.setCharacterEncoding(StandardCharsets.UTF_8.name());InputStream is = resourceDetail.resource.getInputStream();OutputStream os = response.getOutputStream();try (is; os) {// 8 KiB bufferbyte[] buffer = new byte[((1 << 10) << 3)];int bytesRead;while ((bytesRead = is.read(buffer)) > 0) {os.write(buffer, 0, bytesRead);}}} else {if (request.getMethod().equalsIgnoreCase(HttpMethod.GET.name())) {String httpGetQueryString = request.getQueryString();if (httpGetQueryString != null && !httpGetQueryString.isEmpty()) {relativeURL += '?' + httpGetQueryString;}}response.setContentType("application/json;charset=UTF-8");try (Writer writer = response.getWriter()) {writer.write(statService.service(relativeURL));}}}
}

趟坑思路&历程

  1. 直接狂翻Druid官方说明文档,看看有没有SpringMVC拦截数据监控页面的方法(反正我是没有找到)
  2. 度娘一下,看看有没有别人做出来了。结果只找到这个https://blog.csdn.net/weixin_44216706/article/details/106189393 而且还是不知所云的那一类。(鲁迅曾说过,生命是以时间为单位的,浪费别人的时间等于谋财害命。所以,遇到这一类作者,该骂的还得骂!)
  3. 直接使用SpringMVC拦截器 拦截 Druid 内置的 Servlet (无效,因为 拦截器只对 Controller 起作用,通过 ServletRegistrationBean 注册的Servlet不归SpringMVC管。虽然不使用ServletRegistrationBean注册Servlet还有别的办法拦截请求,但太麻烦)
  4. 直接把Druid Jar包里的资源文件原地引用,作为SpringMVC的静态资源,动态资源拦截即可
  5. 直接把Druid Jar包里的资源文件复制出来放到 webapp 文件夹,作为SpringMVC的静态资源。直接指定classpath路径,当前Jar包内引用。然后动态资源拦截即可。(代价是浪费两倍空间,明明 Druid Jar包里就有。而且和官方同步比较麻烦)
  6. 直接使用 Servlet 的 forward 大法转发访问 Druid 内置的 Servlet ,并配置 Druid 只允许本机访问(依然只能本机访问,无法实现代理拦截效果,传到 SpringMVC 的 Controller 层 的 HttpServletRequest 是约定不允许修改的)
  7. 学习使用 SpringCloud里的网关代理 或者 学习使用各大佬实现的现成的 ProxyServlet 工具 转发访问 Druid 内置的 Servlet ,并配置 Druid 只允许本机访问(太复杂,而且session这些问题不好控制。学习成本高,遂放弃)

一开始折腾第四点没成功,因为不熟悉SpringMVC,不知道SpringMVC 的 Resource大法支持通过万能的URL/URI方式引用资源。所以绕了个大弯,还是抱着破罐子破摔万一可行的心态摸索了一下SpringMVC API源码,继续走第四点,看看能不能走得通。现在看来是真的可行。

分享成功的喜悦(发表试验成功的感言):OHHHHHHHHHHHHHHHHHHHHHHH!!!

2022年10月1日更新

发现原来的路子有 BUG,导致一些 API 不能被正常访问。
遂放弃原来的结构,使用 Controller 全面接管,实现了一个简单的 “半Servlet”
(这下应该没有问题了吧……逃)

SpringBoot(SpringMVC)拦截Druid数据监控页面相关推荐

  1. SpringBoot:Mybatis + Druid 数据访问

    SpringBoot:Mybatis + Druid 数据访问 文章目录 SpringBoot:Mybatis + Druid 数据访问 1.简介 2.JDBC 3.CRUD操作 4.自定义数据源 D ...

  2. 数据库连接池 ( 五 ) Druid 数据监控

    4.Druid 数据监控 4.0.Druid 监控参数 参数 值 含义 ActiveCount 0 当前连接池中活跃连接数 ActivePeak 1 连接池中活跃连接数峰值 ActivePeakTim ...

  3. AJAX,SpringMVC,拦截器(Ajax发送请求 经过SpringMVC拦截器重定向其他页面失败)

    Ajax发送请求 经过SpringMVC拦截器重定向其他页面失败 借鉴出处 Ajax是通过异步请求后台,获取数据,局部刷新页面,因此,即使后台进行页面跳转的编码,前台请求完毕以后,只会执行ajax的回 ...

  4. Druid关闭监控页面及设置密码

    Druid关闭监控页面及设置密码 1 spring web.xml配置 1.1 开启监控页面 web.xml 添加 <servlet><servlet-name>druidSt ...

  5. springboot使用thymeleaf完成数据的页面展示

    上一篇介绍了使用jsp完成数据的页面展示 ,但是springboot并不推荐使用jsp,会产生很多问题.官方推荐使用thymeleaf,这里我们将上一篇的jsp页面展示修改为使用thymeleaf,通 ...

  6. 去除alibaba.druid的监控页面

    1.首先有监控页面肯定是配置了 2.找到配置页面:配置文件和代码config都有可能有 3.处理方法: 1删掉那一段代码活配置 2把一些配置改成false(enabled) web-stat-filt ...

  7. Druid关闭监控页面关闭不了

    项目里连接数据库使用了阿里开源的druid,结果被发现有安全漏洞,可以直接访问到druid的监控界面.但是奇怪的是,明明在yml文件里配置了关闭,同时allow的访问IP是127.0.0.1,还设置了 ...

  8. 【夏目鬼鬼分享】springboot搭建阿里Druid数据源监控

    Druid介绍 Druid是一个专为大型数据集上的高性能切片和OLAP分析而设计的数据存储.Druid最常用作为GUI分析应用程序提供动力的数据存储,或者用作需要快速聚合的高度并发API的后端. Dr ...

  9. springboot使用jsp完成数据的页面展示

    上一篇文章介绍了使用JdbcTemplate完成对数据库的增删改查,使用了postman工具测试了功能是否实现,这一篇介绍如何调用springboot的后台接口,将数据真正展示出来.这里使用jsp进行 ...

最新文章

  1. oracle 创建数据库 表空间 用户 授权和toad导入导出数据库
  2. 题解 DTOJ #1438. 矮人排队(lineup)
  3. boost::format模块测试 wchar_t 格式的使用
  4. ECMAScript 2015~2020 语法全解析
  5. PHP的压力测试工具ab.exe 和mpm介绍提高并发数
  6. 人工智能十大算法及应用,十大人工智能算法公司
  7. 迪普交换机恢复出厂设置_迪普产品配置文档-基础篇(2012-11-05).pdf
  8. 计算机房管理制度通知,计算机房管理制度.doc
  9. day 0150面向对象-成员
  10. HTML+CSS大作业:仿小米手机商城网站设计——仿小米手机商城全套(37页) 商城网购HTM5网页设计作业成品
  11. python圣斗士(十七):令人欲罢不能的正则
  12. tewa-800g请输入正确的管理员账户_tp-link路由器如何设置管理员身份绑定 路由器设置管理员身份绑定方法【介绍】...
  13. Redis:MySQL 算老几?
  14. java环境安装完毕,运行web项目报javax annotation managedbean unsup错误
  15. ff7的全部青魔法获得
  16. python画图代码星星-python画星星
  17. 实验一 验证74LS181运算和逻辑功能
  18. 计算机专业转口腔好吗,口腔医学专业VS计算机专业,同样高考志愿大热门,我该选择哪个...
  19. 三阶魔方入门玩法 教程
  20. 安搭Share提醒,谨防秋冬季儿童呼吸道疾病

热门文章

  1. Beego入门简单构建, 连接MySQL实现增查操作
  2. linux图形界面压缩软件,p7zip 压缩软件 for Linux
  3. kdj指标主要看哪个值_kdj主要看哪条线?kdj指标看哪个值。
  4. oracle访问控制策略查看,ORACLE 安全访问策略VPD与ORA-28132
  5. MATLAB强化学习工具箱(一)-在网格环境中使用Q-learning and SARSA
  6. PC电脑装机知识分享
  7. 从电焊女工到Google台湾总经理
  8. 何勉:第一性原理和精益敏捷的规模化实施
  9. 八年级上册历史知识点(第2课 第二次鸦片战争)
  10. 多麦克风做拾音的波束_【语音交互】先从麦克风阵列聊起