绕过 CAS 从 Spring boot 应用程序获取未/安全的健康信息 [英] bypass CAS to get un/secured health infos from Spring boot app
问题描述
我有一个使用 CAS WebSecurity 的 Spring Boot 应用程序,以确保所有传入的未经身份验证的请求都被重定向到一个公共登录页面.
I have a Spring Boot app using CAS WebSecurity to make sure that all incoming non authenticated requests are redirected to a common login page.
@Configuration
@EnableWebSecurity
public class CASWebSecurityConfig extends WebSecurityConfigurerAdapter {
我想通过执行器公开健康端点,并添加了相关的依赖项.我想绕过监控工具将使用的这些/health URL 的 CAS 检查,因此在配置方法中,我添加了:
I want to expose health endpoints through actuator, and added the relevant dependency. I want to bypass the CAS check for these /health URL which are going to be used by monitoring tools, so in the configure method, I have added :
http.authorizeRequests().antMatchers("/health/**").permitAll();
这有效,但现在我想进一步调整它:
This works, but now I want to tweak it further :
- 详细的健康状态(即文档中的完整内容")应该只能由某些特定的监控用户访问,其凭据在属性文件中提供.
- 如果未提供身份验证,则应返回仅状态".
遵循 http://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-monitoring.html#production-ready-health-access-restrictions,我已经按如下方式配置了属性,以便它可以工作:
Following http://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-monitoring.html#production-ready-health-access-restrictions, I've configured the properties as below, so that it should work :
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 ,我在我的配置文件中添加:
But I have a problem with how I configure the credentials... following http://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-monitoring.html#production-ready-sensitive-endpoints , I added in my config file :
security.user.name: admin
security.user.password: secret
但它不起作用 - 当我不放置属性时,我看不到日志中生成的密码.
But it's not working - and when I don't put the properties, I don't see the password generated in logs.
所以我试图放置一些自定义属性,例如
So I'm trying to put some custom properties like
healthcheck.username: healthCheckMonitoring
healthcheck.password: healthPassword
并将这些注入到我的安全配置中,以便 configureGlobal 方法变为:
and inject these into my Security config so that configureGlobal method becomes :
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth,
CasAuthenticationProvider authenticationProvider) throws Exception {
auth.inMemoryAuthentication().withUser(healthcheckUsername).password(healthcheckPassword).roles("ADMIN");
auth.authenticationProvider(authenticationProvider);
}
在配置方法中,我将 URL 模式的配置更改为:
and in the configure method, I change the config for the URL pattern to :
http.authorizeRequests()
.antMatchers("/health/**").hasAnyRole("ADMIN")
.and().httpBasic()
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and().csrf().disable();
使用该配置,我在通过身份验证时获得完整内容,但从逻辑上讲,当我未通过身份验证时,我不会获得任何状态(UP 或 DOWN),因为请求甚至没有到达端点:它被拦截了并被安全配置拒绝.
With that config, I get full content when authenticated, but logically, I don't get any status (UP or DOWN) when I'm not authenticated, because the request doesn't even reach the endpoint : it is intercepted and rejected by the security config.
如何调整我的 Spring Security 配置以使其正常工作?我觉得我应该以某种方式链接配置,首先 CAS 配置允许请求完全基于 URL,以便请求然后点击第二个配置,如果提供凭据,它将执行基本的 http 身份验证,或者让该请求未经身份验证就到达了端点,因此我得到了仅状态"结果..但同时,我认为如果我正确配置 Spring Boot 可以正确管理它..
How can I tweak my Spring Security config so that this works properly ? I have the feeling I should somehow chain the configs, with the CAS config first allowing the request to go through purely based on the URL, so that the request then hits a second config that will do basic http authentication if credentials are provided, or let the request hit the endpoint unauthenticated otherwise, so that I get the "status only" result.. But at the same time, I'm thinking Spring Boot can manage this correctly if I configure it properly..
谢谢!
推荐答案
解决方案不是很好,但到目前为止,这对我有用:
Solution is not great, but so far, that's what works for me :
在我的配置中(只有相关代码):
in my config (only the relevant code):
@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();
}
}
然后我添加了一个特定的过滤器:
Then I added a specific filter :
@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)};
}
}
}
这篇关于绕过 CAS 从 Spring boot 应用程序获取未/安全的健康信息的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!