Search code examples
javamavenspring-bootspring-boot-admin

Spring boot authentication - admin console 403 response to client


I'm using jdk 1.8 and Spring boot 2.1.2.

I would like to enable authentication in administration console of Spring Boot and in its clients.

I setted in Administration application.properties:

spring.security.user.name=admin
spring.security.user.password=secret

spring.boot.admin.discovery.enabled=true

management.endpoints.web.exposure.include=*
management.endpoints.web.cors.allowed-methods=GET,POST

In Administration project I added this class:

@EnableWebSecurity
@Configuration
public class SecuritySecureConfig extends WebSecurityConfigurerAdapter {

    private static final Logger logger = (Logger) LoggerFactory.getLogger(SecuritySecureConfig.class);

    private final String adminContextPath;

    public SecuritySecureConfig(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/**"
            );

    }

}

In administration pom.xml I added:

 <dependencies>
      <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    <dependency>
        <groupId>de.codecentric</groupId>
        <artifactId>spring-boot-admin-starter-server</artifactId>      
    </dependency>

    <dependency>
        <groupId>de.codecentric</groupId>
        <artifactId>spring-boot-admin-server-ui</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-tomcat</artifactId>
    </dependency>

</dependencies>

I was forced to add the annotation @EnableWebFluxSecurity on the main class because without it, it gives an Exception:

org.springframework.beans.factory.support.BeanDefinitionOverrideException: Invalid bean definition with name 'springSecurityFilterChain' defined in class path resource [org/springframework/boot/actuate/autoconfigure/security/reactive/ReactiveManagementWebSecurityAutoConfiguration.class]: Cannot register bean definition [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=org.springframework.boot.actuate.autoconfigure.security.reactive.ReactiveManagementWebSecurityAutoConfiguration; factoryMethodName=springSecurityFilterChain; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [org/springframework/boot/actuate/autoconfigure/security/reactive/ReactiveManagementWebSecurityAutoConfiguration.class]] for bean 'springSecurityFilterChain': There is already [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration; factoryMethodName=springSecurityFilterChain; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [org/springframework/security/config/annotation/web/configuration/WebSecurityConfiguration.class]] bound.

In client application.properties:

spring.security.user.name=joe
spring.security.user.password=my-secret-password

spring.boot.admin.client.username=admin
spring.boot.admin.client.password=secret

spring.boot.admin.client.instance.metadata.user.name=admin
spring.boot.admin.client.instance.metadata.user.password=secret


spring.boot.admin.client.enabled=true

spring.boot.admin.client.auto-registration=true
spring.boot.admin.client.auto-deregistration=true

And in client pom.xml:

 <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-security</artifactId>
 </dependency>

Now if I access both of them using the browser, they prompt me with the login form. I type the login and password and all works as a charm, but the actuator of the client cannot access to the admin, it returns always 403 FORBIDDEN.

2019-02-12 15:21:52.004 - [registrationTask1] DEBUG o.s.core.log.CompositeLog.debug 142 - Response 403 FORBIDDEN

I really cannot understand why the communication between the administration console and the client does not work. Does anyone know where I'm wrong?


Solution

  • I have the same problem so, use

    @EnableWebFluxSecurity
    

    and not

    @EnableWebSecurity
    

    like this

    @Configuration
    @EnableWebFluxSecurity
    public class AppSecurityConfig   {
    
        private final AdminServerProperties adminServer;
    
        public AppSecurityConfig (AdminServerProperties adminServer) {
            this.adminServer = adminServer;
        }
        @Bean
        public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
            http
                .securityMatcher(new NegatedServerWebExchangeMatcher(
                    ServerWebExchangeMatchers.pathMatchers("/instances")))
                .securityMatcher(new NegatedServerWebExchangeMatcher(
                    ServerWebExchangeMatchers.pathMatchers("/actuator/**")))
                .authorizeExchange()
                .anyExchange().authenticated()
                .and()
                .formLogin()
                .loginPage(this.adminServer.getContextPath() + "/login")
                .and()
                .logout()
                .logoutUrl(this.adminServer.getContextPath() + "/logout")
                .and()
                .httpBasic()
                .and()
                .csrf().disable();
            return http.build();
        } }
    

    in you application.yml

    spring:
      security:
        user:
          password: ${ADMIN_PASSWORD}
          name: ${ADMIN_USER}
      application:
        name: Admin Server 
      boot:
        admin:
          client:
            username: ${ADMIN_USER}
            password: ${ADMIN_PASSWORD}
            url: ${ADMIN_SERVER_URL}
            enabled: true
          ui:
            cache:
              no-cache: true
            title: App Monitoring
            instance:
              name: ${spring.application.name}
      main:
        allow-bean-definition-overriding: true
    management:
      endpoints:
        web:
          exposure:
            include: "*"
          cors:
            allowed-origins: "*"
            allowed-methods: GET,POST
      endpoint:
        health:
          show-details: always
    

    It can monitor it self if you want


    in the client side app

    spring:
      boot:
        admin:
          client:
            url: ${ADMIN_SERVER_URL}
            username: ${ADMIN_USER}
            password: ${ADMIN_PASSWORD}
            instance:
              name: ${spring.application.name}
            auto-registration: true
      application:
        name: Client App