0
点赞
收藏
分享

微信扫一扫

Java学习笔记2024/2/16

一、前言

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

二、解决方案

我们创建一个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: health
        whiteUrl:   # 白名单,配置白名单的URL请求时不需要验证,多个可以用英文逗号分隔
    user: admin # 认证用户
    password: '!SM4!-HUYu9S6osKi65pZr7YQO9w=='  # 认证用户密码 SM4Utils.encryptStr("password")
2.4.2 自定义授权过滤器逻辑
import com.learn.SM4Utils;
import org.springframework.util.StringUtils;
import sun.misc.BASE64Decoder;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
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;
    }

    @Override
    public 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=
                    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 {
                        throw new IllegalArgumentException("user or password info error!");
                    }
                } else {
                    throw new IllegalArgumentException("Authorization info must be not null!");
                }
            } else { // 如果没有配置用户名密码则直接放行
                filterChain.doFilter(servletRequest, servletResponse);
            }

        }

    }
}
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;

    @Bean
    public FilterRegistrationBean filterRegistrationBean() {
        FilterRegistrationBean<ActuatorFilter> filterRegistrationBean = new FilterRegistrationBean<>();
        filterRegistrationBean.setFilter(actuatorFilter());
        filterRegistrationBean.addUrlPatterns("/*");
        filterRegistrationBean.setName("ActuatorFilter");
        return filterRegistrationBean;
    }

    @Bean
    public ActuatorFilter actuatorFilter() {
        return new ActuatorFilter(whiteUrl, user, password);
    }

}

2.5 功能测试

在这里插入图片描述

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

举报

相关推荐

0 条评论