Search code examples
springspring-securityspring-security-oauth2keycloakspring-security-rest

HttpSecurity configuration does not work with Keycloak Adapter on Spring


Complete code: https://github.com/czetsuya/Spring-Keycloak-with-REST-API

I'm trying to implement a REST API in Spring secured by Keycloak (4.8.1) with a bearer-only client.

Problem: configure(HttpSecurity http) is not respected and as long as the user is authenticated the REST endpoints are accessible.

For example with .antMatchers("/admin*").hasRole("ADMIN"), /admin should only be accessible by a user with an ADMIN role, but I was able to access using a USER role.

I also tried setting the security-constraints in application.yml (but didn't help):

  security-constraints:
  - auth-roles:
    - ADMIN
  - security-collections:
    - name: admin
    - patterns:
      - /admin*

Using @EnableGlobalMethodSecurity in combination with @PreAuthorize("hasRole('ADMIN')") can do the trick, but is there really no other way around?

Here's the application.xml.

keycloak:
  enabled: true
  realm: dev
  auth-server-url: http://localhost:8083/auth
  ssl-required: external
  resource: dev-api
  bearer-only: true
  confidential-port: 0
  use-resource-role-mappings: false
  principal-attribute: preferred_username

Here are the dependencies on pom:

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

And part of the SecurityConfig class:

@KeycloakConfiguration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter {

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) {
        KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider();
        SimpleAuthorityMapper simpleAuthorityMapper = new SimpleAuthorityMapper();
        simpleAuthorityMapper.setConvertToUpperCase(true);
        keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(simpleAuthorityMapper);
        auth.authenticationProvider(keycloakAuthenticationProvider);
    }

    @Bean
    @Override
    protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
        return new NullAuthenticatedSessionStrategy();
    }

    @Bean
    public KeycloakConfigResolver keycloakConfigResolver() {
        return new KeycloakSpringBootConfigResolver();
    }

    @Autowired
    public KeycloakClientRequestFactory keycloakClientRequestFactory;

    @Bean
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    public KeycloakRestTemplate keycloakRestTemplate() {
        return new KeycloakRestTemplate(keycloakClientRequestFactory);
    }

    /**
     * Secure appropriate endpoints
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        super.configure(http);
        http.authorizeRequests() //
                .antMatchers("/users*").hasRole("USER") //
                .antMatchers("/admin*").hasRole("ADMIN") //
                .anyRequest().authenticated() //
                .and().csrf().disable() //
        ;
    }

After enabling a verbose log. I found out that the security-constraints defined in application.yml are being applied, but not the constraints defined in the java class.

Question now is how to use the java constraints instead of the application.yml defined.


Solution

  • Problem was wrong yml configuration. Here's the correct version:

    security-constraints:
        - auth-roles:
          - User
          security-collections:
          - name: unsecured
            patterns:
            - /users
        - auth-roles: 
          - Admin
          security-collections:
          - name: secured
            patterns:
            - /admin
    

    Note that when a KeycloakSpringBootConfigResolver is defined configure(httpSecurity) will also be invoked.

    Enabling the log below in application.yml will show us the security constraints check:

    logging:
      level:
        org.apache.catalina: DEBUG
    

    Here's a more detailed explanation with code: http://czetsuya-tech.blogspot.com/2018/12/secure-spring-boot-rest-project-with.html