【问题标题】:bypass CAS to get un/secured health infos from Spring boot app绕过 CAS 以从 Spring Boot 应用程序获取不安全/安全的健康信息
【发布时间】:2016-11-25 07:57:29
【问题描述】:

我有一个使用 CAS WebSecurity 的 Spring Boot 应用程序,以确保所有传入的未经身份验证的请求都被重定向到一个公共登录页面。

@Configuration
@EnableWebSecurity
public class CASWebSecurityConfig extends WebSecurityConfigurerAdapter {

我想通过执行器暴露健康端点,并添加相关依赖。我想绕过 CAS 检查这些 /health URL,这些 URL 将被监控工具使用,所以在 configure 方法中,我添加了:

http.authorizeRequests().antMatchers("/health/**").permitAll();

这可行,但现在我想进一步调整它:

  • 详细的健康状态(即文档中的“完整内容”)应该只有某些特定的监控用户才能访问,属性文件中提供了这些用户的凭据。
  • 如果未提供身份验证,则应返回“仅状态”。

http://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-monitoring.html#production-ready-health-access-restrictions 之后,我配置了如下属性,这样它就可以工作了:

management.security.enabled: true
endpoints.health.sensitive: false

但是我对如何配置凭据有疑问...在 http://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-monitoring.html#production-ready-sensitive-endpoints 之后,我在我的配置文件中添加了:

security.user.name: admin
security.user.password: secret

但它不起作用 - 当我不放置属性时,我看不到日志中生成的密码。

所以我正在尝试放置一些自定义属性,例如

healthcheck.username: healthCheckMonitoring
healthcheck.password: healthPassword

并将这些注入到我的安全配置中,以便 configureGlobal 方法变为:

@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth,
                            CasAuthenticationProvider authenticationProvider) throws Exception {

    auth.inMemoryAuthentication().withUser(healthcheckUsername).password(healthcheckPassword).roles("ADMIN");
    auth.authenticationProvider(authenticationProvider);
} 

在配置方法中,我将 URL 模式的配置更改为:

   http.authorizeRequests()
        .antMatchers("/health/**").hasAnyRole("ADMIN")
        .and().httpBasic()
        .and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
        .and().csrf().disable();

使用该配置,我在通过身份验证时获得完整内容,但从逻辑上讲,当我未通过身份验证时,我不会获得任何状态(UP 或 DOWN),因为请求甚至没有到达端点:它被拦截了并被安全配置拒绝。

如何调整我的 Spring Security 配置以使其正常工作?我觉得我应该以某种方式链接配置,CAS 配置首先允许请求完全基于 URL 通过,以便请求然后命中第二个配置,如果提供凭据,该配置将执行基本的 http 身份验证,或者让请求命中端点未经身份验证,因此我得到“仅状态”结果..但同时,我认为如果我正确配置 Spring Boot 可以正确管理它..

谢谢!

【问题讨论】:

    标签: java spring-security spring-boot spring-boot-actuator


    【解决方案1】:

    解决方案不是很好,但到目前为止,这对我有用:

    在我的配置中(仅相关代码):

    @Configuration
    @EnableWebSecurity
    public class CASWebSecurityConfig extends WebSecurityConfigurerAdapter {
    
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //disable HTTP Session management
        http
            .securityContext()
            .securityContextRepository(new NullSecurityContextRepository())
            .and()
            .sessionManagement().disable();
    
        http.requestCache().requestCache(new NullRequestCache());
    
        //no security checks for health checks
        http.authorizeRequests().antMatchers("/health/**").permitAll();
    
        http.csrf().disable();
    
        http
            .exceptionHandling()
            .authenticationEntryPoint(authenticationEntryPoint());
    
        http // login configuration
            .addFilter(authenticationFilter())
            .authorizeRequests().anyRequest().authenticated();
    }
    }
    

    然后我添加了一个特定的过滤器:

    @Component
    public class HealthcheckSimpleStatusFilter  extends GenericFilterBean {
    
    private final String AUTHORIZATION_HEADER_NAME="Authorization";
    
    private final String URL_PATH = "/health";
    
    @Value("${healthcheck.username}")
    private String username;
    
    @Value("${healthcheck.password}")
    private String password;
    
    private String healthcheckRole="ADMIN";
    
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
        throws IOException, ServletException {
        HttpServletRequest httpRequest = this.getAsHttpRequest(request);
    
        //doing it only for /health endpoint.
        if(URL_PATH.equals(httpRequest.getServletPath())) {
    
            String authHeader = httpRequest.getHeader(AUTHORIZATION_HEADER_NAME);
    
            if (authHeader != null && authHeader.startsWith("Basic ")) {
                String[] tokens = extractAndDecodeHeader(authHeader);
                if (tokens != null && tokens.length == 2 && username.equals(tokens[0]) && password.equals(tokens[1])) {
                    createUserContext(username, password, healthcheckRole, httpRequest);
                } else {
                    throw new BadCredentialsException("Invalid credentials");
                }
            }
        }
        chain.doFilter(request, response);
    }
    
    /**
     * setting the authenticated user in Spring context so that {@link HealthMvcEndpoint} knows later on that this is an authorized user
     * @param username
     * @param password
     * @param role
     * @param httpRequest
     */
    private void createUserContext(String username, String password, String role,HttpServletRequest httpRequest) {
        List<GrantedAuthority> authoritiesForAnonymous = new ArrayList<>();
        authoritiesForAnonymous.add(new SimpleGrantedAuthority("ROLE_" + role));
        UserDetails userDetails = new User(username, password, authoritiesForAnonymous);
        UsernamePasswordAuthenticationToken authentication =
            new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
        authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpRequest));
        SecurityContextHolder.getContext().setAuthentication(authentication);
    }
    
    private HttpServletRequest getAsHttpRequest(ServletRequest request) throws ServletException {
        if (!(request instanceof HttpServletRequest)) {
            throw new ServletException("Expecting an HTTP request");
        }
        return (HttpServletRequest) request;
    }
    
    private String[] extractAndDecodeHeader(String header) throws IOException {
        byte[] base64Token = header.substring(6).getBytes("UTF-8");
    
        byte[] decoded;
        try {
            decoded = Base64.decode(base64Token);
        } catch (IllegalArgumentException var7) {
            throw new BadCredentialsException("Failed to decode basic authentication token",var7);
        }
    
        String token = new String(decoded, "UTF-8");
        int delim = token.indexOf(":");
        if(delim == -1) {
            throw new BadCredentialsException("Invalid basic authentication token");
        } else {
            return new String[]{token.substring(0, delim), token.substring(delim + 1)};
        }
    }
    
    }
    

    【讨论】:

      猜你喜欢
      • 2016-05-06
      • 2021-11-06
      • 2019-10-09
      • 1970-01-01
      • 1970-01-01
      • 2021-01-17
      • 2016-12-19
      • 2015-11-15
      • 2014-09-11
      相关资源
      最近更新 更多