所以我自己遇到了这个问题(OAuth + 基本身份验证),最终选择了一条不同的路径,该路径对基本身份验证部分涉及更多,但最终它奏效了。
首先你需要实现你自己的Authentication对象:
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import java.util.Collection;
import java.util.Collections;
public class ActuatorAuthentication implements Authentication {
private static final Object DUMMY = new Object();
private static final String NAME = "Actuator Authenticated";
private static final boolean AUTHENTICATED = true;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return Collections.emptyList();
}
@Override
public Object getCredentials() {
return DUMMY;
}
@Override
public Object getDetails() {
return DUMMY;
}
@Override
public Object getPrincipal() {
return DUMMY;
}
@Override
public boolean isAuthenticated() {
return authenticated;
}
@Override
public void setAuthenticated(boolean b) throws IllegalArgumentException {
throw new UnsupportedOperationException("Operation setAuthenticated not supported.");
}
@Override
public String getName() {
return NAME;
}
}
我们将使用它与AutoConfiguredHealthEndpointGroup.isAuthorized() 方法一起使用,以便在我们配置以下内容时切换我们想要的行为:
management:
endpoint:
health:
show-details: when_authorized
因此,我们将使用它来实现我们自己的OncePerRequestFilter,它将对除/health、/health/liveness 和/health/readiness 之外的所有执行器端点执行实际的基本身份验证。
如果未提供基本身份验证,我们还将确保仅 /health 将返回基本 UP 或 DOWN。
这个代码最终是:
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
public class ActuatorSecurityFilter extends OncePerRequestFilter {
private static final String[] EXCLUDED_ENDPOINTS = {
"/actuator/health/liveness",
"/actuator/health/readiness",
"/actuator/info"
};
private AntPathMatcher pathMatcher = new AntPathMatcher();
private final String username;
private final String password;
public ActuatorSecurityFilter(String username, String password) {
this.username = username;
this.password = password;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
boolean authorized = false;
String authorization = request.getHeader("Authorization");
if (authorization != null) {
// Authorization: Basic base64credentials
String base64Credentials = authorization.substring("Basic".length()).trim();
byte[] credDecoded = Base64.getDecoder().decode(base64Credentials);
String credentials = new String(credDecoded, StandardCharsets.UTF_8);
// credentials = username:password
final String[] values = credentials.split(":", 2);
if (values.length == 2 && username.equals(values[0]) && password.equals(values[1])) {
authorized = true;
}
}
// Handle specific non-auth vs. auth for actuator health
if (pathMatcher.match("/actuator/health", request.getRequestURI())) {
if (authorized) {
// Utilize Dummy Authentication to show full details
Authentication authentication = new ActuatorAuthentication();
SecurityContextHolder.getContext().setAuthentication(authentication);
} else {
// Ensure no authentication is provided to show basic details if auth is incorrect
SecurityContextHolder.getContext().setAuthentication(null);
}
} else if (!authorized) {
response.sendError(401);
return;
}
chain.doFilter(request, response);
}
@Override
protected boolean shouldNotFilter(HttpServletRequest request) {
for (String endpoint : EXCLUDED_ENDPOINTS) {
if (pathMatcher.match(endpoint, request.getRequestURI())) {
return true;
}
}
return false;
}
}
最后我们想把它和我们自己的配置类连接起来:
import com.cantire.sps.application.filter.ActuatorSecurityFilter;
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 ActuatorSecurityConfiguration {
@Value("${actuator.username}")
private String username;
@Value("${actuator.password}")
private String password;
@Bean
FilterRegistrationBean<ActuatorSecurityFilter> securityFilterRegistration() {
FilterRegistrationBean<ActuatorSecurityFilter> registration = new FilterRegistrationBean<>();
registration.setFilter(new ActuatorSecurityFilter(username, password));
registration.addUrlPatterns("/actuator/*");
return registration;
}
}
然后我们只需将其添加到您的应用程序属性中:
actuator:
username: monitor
password: password
瞧,无论您实施了什么 WebSecurityConfigurerAdapter,您都可以为您的 Actuator 端点使用基本身份验证。