Spring 启动管理服务器无法访问端点(未授权 401)

Spring Boot Admin Server can't get access to endpoints (Unauthorized 401)

我已经配置了我自己的 in-memory 身份验证配置,我的应用程序可以在 Spring 启动管理服务器上自行注册并且服务器获得了正确的凭据,但它仍然从我的应用程序那里得到未经授权的响应.如果我在浏览器中输入凭据,那么它就可以工作了。

@Configuration
@Order(2)
public class ActuatorSecurity extends WebSecurityConfigurerAdapter {
  @Value("${spring.boot.admin.client.instance.metadata.user.name:actuator}")
  private String actuatorName;
  @Value("${spring.boot.admin.client.instance.metadata.user.password:secret}")
  private String actuatorPassword;

  @Override
  protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.inMemoryAuthentication().withUser(actuatorName).password("{noop}" + actuatorPassword).authorities("ACTUATOR");
  }

  @Override
  public void configure(HttpSecurity http) throws Exception {
    http
            .antMatcher("/actuator/**")
            .authorizeRequests()
            .anyRequest().hasAuthority("ACTUATOR")
            .and()
            .httpBasic();
  }
}

spring-boot-admin-dashboard

有效的浏览器请求与导致 401 的 Spring-Boot-Admin 请求之间的区别在于 BasicAuthenticationFilter 在浏览器尝试中获得 header 而在 Spring-Boot-Admin 尝试中BasicAuthenticationFilter 不阅读任何内容 header 并生成匿名用户。

有什么想法吗?

我遇到了类似的问题,并使用以下示例(来自文档)解决了。

@Bean
public HttpHeadersProvider customHttpHeadersProvider() {
    return  instance -> {
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.add("Authorization", "My Custom Value");
        return httpHeaders;
    };
}

In case you need to inject custom HTTP headers into the requests made to the monitored application’s actuator endpoints you can easily add a HttpHeadersProvider

参考: Injecting Custom HTTP Headers

解决方法是使用自定义 headers 为 /actuator/** 路径构建自己的过滤器,如下所示:

应用端:

@Configuration
@Order(2)
public class ActuatorSecurity extends WebSecurityConfigurerAdapter {

  public static String name = "actuator-admin";
  public static String pw = "actuator-pw";

  public static String headerName = "ACTUATOR_HEADER_NAME";
  public static String headerPw = "ACTUATOR_HEADER_PW";


  protected String getActuatorFilterUrl() {
    return "/actuator/" + "**";
  }

  @Override
  protected void configure(HttpSecurity httpSecurity) throws Exception {
    httpSecurity
            .cors()
            .and()
            // we don't need CSRF because our token is invulnerable
            .csrf().disable()
            // All urls must be authenticated (filter for token always fires (/**)
            .antMatcher(getActuatorFilterUrl())

            .authorizeRequests()
            .antMatchers(HttpMethod.OPTIONS).permitAll()
            .antMatchers(getActuatorFilterUrl()).authenticated()

            .and()
            .addFilterBefore(new ActuatorSecurityFilter(getActuatorFilterUrl(), name, pw, headerName, headerPw),
                    UsernamePasswordAuthenticationFilter.class)
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
  }
}

public class ActuatorSecurityFilter extends AbstractAuthenticationProcessingFilter {

  private String name;
  private String pw;

  private String headerName;
  private String headerPw;

  public ActuatorSecurityFilter(String filterUrl, String name, String pw, String headerName, String headerPw) {
    super(filterUrl);
    this.name = name;
    this.pw = pw;

    this.headerName = headerName;
    this.headerPw = headerPw;
  }

  @Override
  public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse httpServletResponse) throws AuthenticationException, IOException, ServletException {
    final String name = request.getHeader(headerName);
    final String pw = request.getHeader(headerPw);

    if (name.equals(this.name) && pw.equals(this.pw)) {
      return new Authentication() {
        @Override
        public Collection<? extends GrantedAuthority> getAuthorities() {
          return new ArrayList<>();
        }

        @Override
        public Object getCredentials() {
          return null;
        }

        @Override
        public Object getDetails() {
          return null;
        }

        @Override
        public Object getPrincipal() {
          return null;
        }

        @Override
        public boolean isAuthenticated() {
          return true;
        }

        @Override
        public void setAuthenticated(boolean b) throws IllegalArgumentException {

        }

        @Override
        public String getName() {
          return null;
        }
      };
    }
    throw new IllegalStateException("name or pw wrong");
  }

  @Override
  protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult)
          throws IOException, ServletException {

    SecurityContextHolder.getContext().setAuthentication(authResult);

    // As this authentication is in HTTP header, after success we need to continue the request normally
    // and return the response as if the resource was not secured at all
    chain.doFilter(request, response);
  }
}

Spring-Admin 一侧:

@Configuration
public class CUstomHeaderConf {

  @Bean
  public HttpHeadersProvider customHttpHeadersProvider() {
    return  instance -> {
      HttpHeaders httpHeaders = new HttpHeaders();
      httpHeaders.add("ACTUATOR_HEADER_NAME", "actuator-admin");
      httpHeaders.add("ACTUATOR_HEADER_PW", "actuator-pw");
      return httpHeaders;
    };
  }
}