Noe Lopez
Posted on April 9, 2023
What is Lambda DSL?
In this short article, you will learn how to use Spring Security Lambda DSL. It is a new approach to configure web security in Spring projects using lambda expresions. One of the main benefits of it is to avoid chaining using the and() method. It also promotes cleaner coding style in my opinion.
Of course, the chaining style is still valid and the use of lambda expresions is optional. After reading this article, you will know what is going on whenever you see any of them.
What we are going to show in the next example is the same configuration with both techniques. Without any further delay, lets walk through some code.
Securing our REST application
For this exercise, we have two rest endpoints (check out my http client articles for more info on them here) that need to be protected and only authorized users can call it. The first is the customers endpoint and the second one is the documents service. There are available in the below URLs:
Adding security to our project
The first thing to do to add security to an existing Spring Boot project is to include the security dependency in the pom.xml.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
At the time of this writing the latest stable Spring Boot version is 3.0.5. Next step is to create a config class where security rules will be set up. This class will be placed in a package named security and it has to be annotated with @Component and @EnableWebSecurity. Be aware that this is the new way to setup security since Spring 2.7.0 as WebSecurityConfigurerAdapter has been deprecated.
@Configuration
@EnableWebSecurity
public class HttpBasicSecurityConfig {
...
}
Now a bean annotated method of type SecurityFilterChain must be added. This method takes an HttpSecurity class that allows configuring web based security for specific http requests. By default it will be applied to all requests. The default username user and a password is generated automatically when the application is started which can be found in the app log file.
Using generated security password: 0b65bdbd-6164-44a2-993f-6bd4baa5c40b
This generated password is for development use only. Your security configuration must be updated before running your application in production.
Here what we want is to protect each rest api with different credentials. Code is displayed below:
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http
1. .authorizeHttpRequests()
2. .requestMatchers("/").permitAll()
3. .requestMatchers("/api/v1/customers/**").hasRole("USER")
4. .requestMatchers("/api/v1/documents/**").hasRole("ADMIN")
5. .anyRequest().authenticated()
6. .and().csrf().disable()
7. .sessionManagement().disable()
8. .httpBasic()
9. .build();
}
Let's go through each line of the above snippet.
- Line 1: authorizeHttpRequests method will help to create the authorize rules
- Line 2: First rule is to allow access to all paths/URLs. Note that the order of the rules is quite important. They are process by Spring as they are defined (top-down). Normally, wider rules comes first.
- Line 3: Second rule applies to all paths starting with /api/v1/customers. They must have the role USER so that access is granted.
- Line 4: Third rule applies to all paths starting with /api/v1/documents. They must have the role ADMIN so that access is granted.
- Line 5: Any other request must be authenticated
- Line 6: Here is where chaining comes in. More config is needed like disabling csfr
- Line 7: Disable session management as it is not needed for our REST app.
- Line 8: Authentication mechanism is HTTP basic.
- Line 9: Build the HttpSecurity object.
Finally, user accounts are created for each ROLE defined in the securityFilterChain method. As this is a simple app, they are created in memory. In a production environment credentials would be verified from a database or LDAP.
@Bean
public InMemoryUserDetailsManager userDetailsService() {
UserDetails user = User.withDefaultPasswordEncoder()
.username("user1234")
.password("password5678")
.roles("USER")
.build();
UserDetails admin = User.withDefaultPasswordEncoder()
.username("admin1234")
.password("password5678")
.roles("ADMIN")
.build();
return new InMemoryUserDetailsManager(user, admin);
}
Lambda DSL
Let's rewrite the above case with the new lambda syntax. The functionality is exactly the same.
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http
1. .authorizeRequests(auth -> {
2. auth.requestMatchers("/").permitAll();
3. auth.requestMatchers("/api/v1/customers/**").hasRole(
"USER");
4. auth.requestMatchers("/api/v1/documents/**").hasRole(
"ADMIN");
5. auth.anyRequest().authenticated();
})
6. .csrf(csrf -> csrf.disable())
7. .sessionManagement(session ->
session.sessionCreationPolicy(
SessionCreationPolicy.STATELESS))
8. .httpBasic(it -> {})
9. .build();
}
Let's take a closer look at the above code.
In line 1 authorizeRequests method takes a Customizer type which is a functional interface accepting a generic T parameter. Hence, a lambda expression can be used. The braces are needed as it performs multiple actions. Lines 2 to 5 are the very same rules as the chaining style. Also, authorizeRequests method returns an HttpSecurity instance meaning it can be re-used after the call. That is why there is not need to chain with and().
Lines 6-8 disable csfr, disable session and set up http basic (Using the defaults).
Conclusion
In this article we have learned a new way to declare Spring security config. Both styles have been presented, so now it is up to you which one to pick.
Thanks for reading and subscribe to get more useful Java/Spring content.
Image by 3d Vetores por Vecteezy
Posted on April 9, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.