问题场景:
目前SpringBoot Actuator暴露出的端点是未经授权就可以直接访问的,这样可能会存在一些安全隐患
解决方法
1.第一种方案:可以在配置文件中把暴露出的端点排除掉
#开启所有的端点访问(目前 health和info是默认开启的)
management.endpoints.web.exposure.include=*
#屏蔽掉health端点
management.endpoints.web.exposure.exclude=health
这种方案屏蔽掉之后,就不能在访问了,对于项目上仍然想查看这些数据的是不太友好的
2.第二种方案:增加一个过滤器,对这些端点进行权限验证
过滤器代码如下:
package com.zhoule.demoactuator.filter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Base64;
import java.util.Enumeration;
public class ActuatorFilter implements Filter {
public static final Logger logger = LoggerFactory.getLogger(ActuatorFilter.class);
public static final String ACTUATOR_SESSION = "ActuatorBasicAuthSession";
private String userName;
private String password;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
Enumeration<String> enumeration = filterConfig.getInitParameterNames();
if (enumeration.hasMoreElements()) {
this.userName = filterConfig.getInitParameter("actuatorUserName");
this.password = filterConfig.getInitParameter("actuatorPassword");
}
logger.info("ActuatorFilter init success");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
String path = request.getRequestURI();
//可以把所有暴露出来的端点路径放在一个集合里面进行判断
if (path.startsWith("/actuator")) {
Object actuatorSessionValue = request.getSession().getAttribute(ACTUATOR_SESSION);
if (actuatorSessionValue == null || !userName.equals(actuatorSessionValue)) {
String auth = request.getHeader("Authorization");
if (auth != null && !"".equals(auth)) {
//解析认证参数
String userAndPass = this.decodeBase64(auth.substring(6));
String[] upArr = userAndPass.split(":");
if (upArr.length != 2) {
this.writeForbiddenCode(response);
return;
}
String iptUser = upArr[0];
String iptPass = upArr[1];
if (iptUser.equals(this.userName) && iptPass.equals(this.password)) {
request.getSession().setAttribute(ACTUATOR_SESSION, this.userName);
filterChain.doFilter(request, response);
return;
}
this.writeForbiddenCode(response);
return;
} else {
this.writeForbiddenCode(response);
return;
}
}
}
filterChain.doFilter(request, response);
}
/**
* 未认证时返回401认证页面
*
* @param httpServletResponse
* @throws IOException
*/
private void writeForbiddenCode(HttpServletResponse httpServletResponse) throws IOException {
httpServletResponse.setStatus(401);
httpServletResponse.setHeader("WWW-Authenticate", "Basic realm=\"input Actuator Basic userName & password \"");
httpServletResponse.getWriter().write("You do not have permission to access this resource");
}
private String decodeBase64(String source) {
String decodeStr = null;
if (source != null) {
try {
byte[] bytes = Base64.getDecoder().decode(source);
decodeStr = new String(bytes);
} catch (Exception var4) {
this.logger.error(var4.getMessage(), var4);
}
}
return decodeStr;
}
}
Config配置类
package com.zhoule.demoactuator.config;
import com.zhoule.demoactuator.filter.ActuatorFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Bean
public FilterRegistrationBean actuatorFilter () {
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(new ActuatorFilter());
registrationBean.setName("actuator");
registrationBean.addUrlPatterns("/*");
registrationBean.setOrder(Integer.MAX_VALUE);
//用户名密码可以配置在配置文件中或者配置中心
registrationBean.addInitParameter("actuatorUserName", "admin");
registrationBean.addInitParameter("actuatorPassword", "123456");
return registrationBean;
}
}
访问效果
1.加了授权之后的访问页面
2.输入了正确的用户名和密码之后