Gostaria de uma sugestão para implementar segurança no endpoint do actuator (tira-lo do .antMatchers) para que somente o projeto do sprint-boot-admin possa consulta-lo.
Gostaria de uma sugestão para implementar segurança no endpoint do actuator (tira-lo do .antMatchers) para que somente o projeto do sprint-boot-admin possa consulta-lo.
Oi Igor,
Desculpe a demora em responder.
Para fazer o que vocé disse, vai precisar das seguintes alterações no projeto:
Na classe SecurityConfigurations
remover o antMatchers do actuator:
http.authorizeRequests()
.antMatchers(HttpMethod.GET, "/topicos").permitAll()
.antMatchers(HttpMethod.GET, "/topicos/*").permitAll()
.antMatchers(HttpMethod.POST, "/auth").permitAll()
.anyRequest().authenticated()
.and().csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and().addFilterBefore(new AutenticacaoViaTokenFilter(tokenService, usuarioRepository), UsernamePasswordAuthenticationFilter.class);
Mas é necessário que os endpoints do actuator tenham segurança, portanto podemos criar outra classe com as regras de segurança especificas do actuator:
@Configuration
@Order(1)
public class ActuatorSecurityConfigurations extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("actuator").password(passwordEncoder().encode("actuator123"))
.authorities("ROLE_ADMIN");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/actuator/**").hasRole("ADMIN")
.and().httpBasic();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
Repare que em cima da classe tem o @Order(1)
. Isso é para indicar ao Spring que essa classe deve ser carregada antes da classe SecurityConfigurations
, que vai precisar ter @Order(2)
:
@EnableWebSecurity
@Configuration
@Order(2)
public class SecurityConfigurations extends WebSecurityConfigurerAdapter {
//resto do codigo...
No application.properties
do projeto monitoramento
é necessário também ter segurança:
server.port=8081
spring.security.user.name=admin
spring.security.user.password=admin123
E para conseguir fazer login no projeto de monitoramento é necessário adicionar mais 2 dependências no pom.xml
do projeto monitoramento
:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-server-ui-login</artifactId>
<version>1.5.7</version>
</dependency>
E no projeto monitoramento criar a classe SecurityConfigurations
:
@Configuration
public class SecurityConfigurations extends WebSecurityConfigurerAdapter {
private final String adminContextPath;
public SecurityConfigurations(AdminServerProperties adminServerProperties) {
this.adminContextPath = adminServerProperties.getContextPath();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
SavedRequestAwareAuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
successHandler.setTargetUrlParameter("redirectTo");
successHandler.setDefaultTargetUrl(adminContextPath + "/");
http.authorizeRequests()
.antMatchers(adminContextPath + "/assets/**").permitAll()
.antMatchers(adminContextPath + "/login").permitAll()
.anyRequest().authenticated()
.and().formLogin()
.loginPage(adminContextPath + "/login").successHandler(successHandler)
.and().logout().logoutUrl(adminContextPath + "/logout")
.and().httpBasic()
.and().csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.ignoringAntMatchers(adminContextPath + "/instances", adminContextPath + "/actuator/**");
}
}
E por fim, no application properties
do projeto forum
, adicionar essas configurações:
# spring boot admin server
spring.boot.admin.client.url=http://localhost:8081
#login/senha do admin-server:
spring.boot.admin.client.username=admin
spring.boot.admin.client.password=admin123
#login/senha do client:
spring.boot.admin.client.instance.metadata.user.name=actuator
spring.boot.admin.client.instance.metadata.user.password=actuator123
Agora ao tentar acessar o endpoint do actuator (http://localhost:8080/actuator
) será exigido login/senha: admin/admin123
E ao tentar entrar na tela de monitoramento(http://localhost:8081
) também será necessário fazer login: actuator/actuator123
Tudo isso está na documentação do Spring Boot Admin
: http://codecentric.github.io/spring-boot-admin/2.1.4/#securing-spring-boot-admin
Bons estudos!
Boa noite Rodrigo... eu segui a documentação porém quando eu coloco o spring security no projeto do SBA eu recebo o seguinte erro quando eu subo o SBA:
Failed to register application as Application: 401 null. Further attempts are logged on DEBUG level
Seguindo a sua dica aqui também parei no mesmo erro :/
Oi Igor,
Eu testei aqui e realmente deu esse mesmo erro =/
Pesquisei e testei várias coisas, mas também não rolou.
Acredito que possa ser então algum problema de compatibilidade entre as versões do Spring Boot, Spring Boot Admin Server e Spring Boot Admin Client.
Teria que testar usando outras versoes mais antigas pra ver.
Então pensei a mesma coisa... O que eu tentei fazer que também falava na documentação foi criar meu proprio header. Eu crieium JwtProvider no SBA com a minha secret key. Sobreescrevi o bean HttpHeaderProvider colocando o meu jwt nos headers. Eu vi que era possível sobreescrever porque na classe AdminServerAutoConfiguration temos esses métodos:
@Bean
@Primary
@ConditionalOnMissingBean
public CompositeHttpHeadersProvider httpHeadersProvider(Collection<HttpHeadersProvider> delegates) {
return new CompositeHttpHeadersProvider(delegates);
}
@Bean
@Order(0)
@ConditionalOnMissingBean
public BasicAuthHttpHeaderProvider basicAuthHttpHeadersProvider() {
return new BasicAuthHttpHeaderProvider();
}
A minha idéia com base em algumas pesquisas foi na classe Main criar um método com @Bean (pensei que desse para sobreescrever pelo fato dos providers do autoconfiguration conterem a anotação @ConditionalOnMissingBean) mais ou menos assim:
@Bean
public HttpHeaderProvider httpHeadersProvider() {
return new JwtAuthHttpHeaderProvider();
}
Onde o JwtAuthHttpHeaderProvider é uma classe minha que implementa a interface HttpHeaderProvider e coloca com header.add() meu JWT no header. Porém ao subir o SBA em debug e colocar o breakpoint no método "instanceWebClient" da classe AdminServerAutoConfiguration vi que o HttpProvider que ele ta pegando é o CompositeHttpProvider e não o que eu configurei na classe main como sendo o meu bean... Você teria alguma idéia de porque não está pegando meu "custom" bean? Obs: para poder ter na classe main o bean com mesmo nome eu tive que colocar no application.properties a configuracao:
spring.main.allow-bean-definition-overriding=true
Oi Igor,
Bom, se a sobrescrita não funcionou, você pode tentar usar a anotação @AutoConfigureBefore
, pois pode ter sido algum conflito em relação a ordem de carregamento das classes.
Testei aqui e não funcionou... eu abri um "issue" la no git referente a esse nosso problema... Você consegue imaginar algum outro jeito em que possamos "injetar" na collection delegates do método
public CompositeHttpHeadersProvider httpHeadersProvider(Collection<HttpHeadersProvider> delegates)
Ja que sabemos que no auto-configuration ele chama esse método, se conseguíssemos adicionar nessa collection o nosso próprio HttpHeaderProvider acredito que resolveria o nosso problema..
Já que ele sempre está chamando o CompositeHttpHeadersProvider
default, não rola de você apenas adicionar o seu na lista?
Algo como:
@Bean
public CompositeHttpHeadersProvider httpHeadersProvider(Collection<HttpHeadersProvider> delegates) {
delegates.add(new JwtAuthHttpHeaderProvider());
return new CompositeHttpHeadersProvider(delegates);
}
Tentei aqui mas não funcionou..... tentei algumas outras coisas também mas nada de conseguir fazer o sba usar esse meu bean customizado :/
Rodrigo consegui fazer funcionar. O problema estava na classe Main que usamos pra rodar o spring boot, junto das anotações de @Configuration, @EnableAutoConfiguration e @EnableAdminServer. Tive que adicionar a anotação
@Import({SecurityConfigurations.class})
Ficando então:
@Configuration
@EnableAutoConfiguration
@EnableAdminServer
@Import({SecurityConfigurations.class})
public class MonitoramentoApplication {
public static void main(String[] args) {
SpringApplication.run(MonitoramentoApplication.class, args);
}
}
Lendo a documentação dessa anotação ela diz "Indicates one or more @Configuration classes to import.
A unica coisa que eu não consegui entender é quando precisamos usar essa anotação ou não, pois no outro projeto que está sendo monitorado (o client) eu não precisei configurar minha classe main com a anotação @Import importando a minha classe de configuração do Spring Security...
Opa Igor,
Que bom que resolveu! Foi tenso esse problema :D
Pois é, o Spring Boot já deveria carregar as classes de configuração automaticamente...
Só se o SBA faz alguma coisa que desabilita o carregamento dessas classes por algum motivo.
Bons estudos!
Hmm entendi, obrigado :)