Kotlin Configuration

Spring Security Kotlin configuration has been available since Spring Security 5.3. It lets users configure Spring Security by using a native Kotlin DSL.

Spring Security provides a sample application to demonstrate the use of Spring Security Kotlin Configuration.

HttpSecurity

How does Spring Security know that we want to require all users to be authenticated? How does Spring Security know we want to support form-based authentication? There is a configuration class (called SecurityFilterChain) that is being invoked behind the scenes. It is configured with the following default implementation:

import org.springframework.security.config.annotation.web.invoke

@Bean
open fun filterChain(http: HttpSecurity): SecurityFilterChain {
    http {
        authorizeHttpRequests {
            authorize(anyRequest, authenticated)
        }
        formLogin { }
        httpBasic { }
    }
    return http.build()
}
Make sure to import the invoke function in your class, as the IDE will not always auto-import the method, causing compilation issues.

The default configuration (shown in the preceding listing):

  • Ensures that any request to our application requires the user to be authenticated

  • Lets users authenticate with form-based login

  • Lets users authenticate with HTTP Basic authentication

Note that this configuration parallels the XML namespace configuration:

<http>
	<intercept-url pattern="/**" access="authenticated"/>
	<form-login />
	<http-basic />
</http>

Multiple HttpSecurity Instances

We can configure multiple HttpSecurity instances, just as we can have multiple <http> blocks. The key is to register multiple SecurityFilterChain @Beans. The following example has a different configuration for URLs that start with /api/:

import org.springframework.security.config.annotation.web.invoke

@Configuration
@EnableWebSecurity
class MultiHttpSecurityConfig {
    @Bean                                                            (1)
    public fun userDetailsService(): UserDetailsService {
        val users: User.UserBuilder = User.withDefaultPasswordEncoder()
        val manager = InMemoryUserDetailsManager()
        manager.createUser(users.username("user").password("password").roles("USER").build())
        manager.createUser(users.username("admin").password("password").roles("USER","ADMIN").build())
        return manager
    }

    @Order(1)                                                        (2)
    @Bean
    open fun apiFilterChain(http: HttpSecurity): SecurityFilterChain {
        http {
            securityMatcher("/api/**")                               (3)
            authorizeHttpRequests {
                authorize(anyRequest, hasRole("ADMIN"))
            }
            httpBasic { }
        }
        return http.build()
    }

    @Bean                                                            (4)
    open fun formLoginFilterChain(http: HttpSecurity): SecurityFilterChain {
        http {
            authorizeHttpRequests {
                authorize(anyRequest, authenticated)
            }
            formLogin { }
        }
        return http.build()
    }
}
1 Configure Authentication as usual.
2 Create an instance of SecurityFilterChain that contains @Order to specify which SecurityFilterChain should be considered first.
3 The http.securityMatcher states that this HttpSecurity is applicable only to URLs that start with /api/
4 Create another instance of SecurityFilterChain. If the URL does not start with /api/, this configuration is used. This configuration is considered after apiFilterChain, since it has an @Order value after 1 (no @Order defaults to last).