skip to content

What is the Lambda DSL in Spring Security and why is it preferred over the old chained-and method style?

level: middleimportance: must knowfreq 70%

answer

  1. Customizer lambda per feature
  2. No more .and() navigation
  3. Indentation = scope
  4. Customizer.withDefaults() enables with defaults
  5. Non-lambda deprecated 6.1, gone in 7.0

basics

~20 s

The Lambda DSL configures each area by passing a lambda (a Customizer) into methods like authorizeHttpRequests(auth -> ...). It groups related settings clearly and avoids the ambiguous .and() chaining of the old style, which is now deprecated.

solid answer

~40 s

The Lambda DSL configures HttpSecurity by passing a Customizer lambda to each configurer — e.g. http.csrf(csrf -> csrf.disable()).authorizeHttpRequests(auth -> auth.anyRequest().authenticated()). Each lambda receives that configurer's own DSL object, so nesting visually scopes the settings and you never need .and() to return to the top level; you just chain the next top-level method. The old style called things like http.authorizeRequests().antMatchers(...).permitAll().and().csrf().disable(), where .and() navigated back up an implicit tree — easy to misread and misplace. Lambda DSL removes that ambiguity, improves indentation-based readability, and is auto-migratable. Since Spring Security 6.1 the non-lambda methods are deprecated, and in 7.0 the Lambda DSL is the only supported form. Use Customizer.withDefaults() when you want a feature enabled with no custom tweaks.

code

java · 11 lines
java
// Modern Lambda DSL
@Bean
SecurityFilterChain chain(HttpSecurity http) throws Exception {
    http
        .csrf(csrf -> csrf.disable())
        .authorizeHttpRequests(auth -> auth
            .requestMatchers("/api/public/**").permitAll()
            .anyRequest().authenticated())
        .httpBasic(Customizer.withDefaults());
    return http.build();
}

go deeper

for a junior

Recognizes the lambda shape and that .and() is old style.

for a middle

Explains Customizer scoping, withDefaults() semantics, and the readability win.

for a senior

Knows the deprecation timeline (6.1 deprecated, 7.0 only lambda) and can migrate code.

for a principal

Frames the DSL evolution as reducing configuration foot-guns and drives team-wide migration before 7.0.

**What the Lambda DSL is.** `HttpSecurity` exposes each security feature through a configurer — authorization, CSRF, CORS, form login, HTTP Basic, session management, etc. The **Lambda DSL** means each of these methods takes a single argument: a `Customizer<T>`, i.e. a lambda `t -> { ... }` that receives that feature's own configuration object and mutates it. Example: ```java http .csrf(csrf -> csrf.disable()) .cors(Customizer.withDefaults()) .authorizeHttpRequests(auth -> auth .requestMatchers("/login").permitAll() .anyRequest().authenticated()) .formLogin(form -> form.loginPage("/login").permitAll()); ``` Each top-level method (`csrf`, `cors`, `authorizeHttpRequests`, `formLogin`) returns the *same* `HttpSecurity` so you keep chaining top-level methods, while the *inner* lambda scopes settings to that one feature. **The old chained style it replaces.** Pre-lambda, the same config looked like: ```java http .authorizeRequests() .antMatchers("/login").permitAll() .anyRequest().authenticated() .and() .formLogin() .loginPage("/login").permitAll() .and() .csrf().disable(); ``` Here every configurer method returned the *nested* configurer, and **`.and()`** climbed back up to `HttpSecurity` so you could start the next feature. Miscounting `.and()` calls, or forgetting one, silently attached settings to the wrong configurer — a classic source of bugs. **Why lambda is preferred.** - **No `.and()` navigation.** The lambda's closing brace ends the feature; there is no implicit tree to climb. - **Visual scoping.** Indentation shows exactly which settings belong to which feature. - **Consistency.** Every feature is configured the same way. - **`Customizer.withDefaults()`** is a tiny built-in lambda meaning "enable this with defaults" — e.g. `http.httpBasic(Customizer.withDefaults())`. **Version timeline (important for interviews).** - 5.2: Lambda DSL introduced (optional). - 5.7: `WebSecurityConfigurerAdapter` deprecated; bean-based config becomes idiomatic. - 6.0: adapter removed. - **6.1: the non-lambda (chained + `.and()`) configuration methods are deprecated.** - **7.0: Lambda DSL is the only supported style; `.and()` overloads removed.** **Gotchas.** - Don't mix styles — calling `.and()` inside a lambda is a code smell and won't be available in 7.0. - `Customizer.withDefaults()` still *enables* the feature; it is not a no-op. To turn something off you call its `disable()` inside the lambda (e.g. `csrf -> csrf.disable()`). - The lambda receives the configurer, not `HttpSecurity`; you cannot call unrelated top-level methods inside it. **When to use.** Always, in 5.7+. The chained style is legacy and being removed.

  • What does Customizer.withDefaults() do — is it a no-op?
    No. It is a lambda that leaves the feature at its default settings but still enables it. For example, httpBasic(Customizer.withDefaults()) turns HTTP Basic on; to disable a feature you call its disable() inside the lambda instead.
  • Why did the old style need .and() and why is that error-prone?
    Each nested configurer method returned the nested object, so .and() was required to climb back to HttpSecurity for the next feature. Miscounting or omitting .and() silently applied settings to the wrong configurer, causing subtle security bugs.

saying these in an interview costs you the question

  • Claiming Customizer.withDefaults() disables a feature
  • Mixing .and() inside lambda DSL
  • Thinking Lambda DSL and bean-based config are the same thing (they are orthogonal)

context