Spring Security Introduction
What Spring Security handles, the security filter chain, and a modern SecurityFilterChain bean example.
What Spring Security handles
Spring Security is a framework for two related but distinct concerns:
- Authentication — verifying who is making a request (a valid username/password, a valid token, a valid client certificate).
- Authorization — deciding what an authenticated (or anonymous) request is allowed to do (can this user view this resource? can they delete it?).
Both are implemented as a chain of servlet filters that every request passes through before it ever reaches your controller code — meaning security decisions happen consistently, in one place, rather than being re-implemented inside every endpoint.
The security filter chain
Spring Security inserts itself into the servlet request pipeline as a series of filters, each responsible for one concern, evaluated in a fixed order for every incoming request:
Request
│
v
┌──────────────────────────┐
│ SecurityContextFilter │ loads/stores the current Authentication
├──────────────────────────┤
│ Authentication filter(s) │ e.g. checks a login form, or validates a JWT
├──────────────────────────┤
│ Authorization filter │ checks the authenticated principal against access rules
├──────────────────────────┤
│ Your controller │ only reached if authorization passes
└──────────────────────────┘
│
v
Response (or 401/403 short-circuit before ever reaching the controller)
If any filter rejects the request — no valid credentials, or a valid principal without permission — the chain short-circuits with 401 Unauthorized or 403 Forbidden and the controller never runs.
Configuring the chain: SecurityFilterChain
Since Spring Security 6 (Spring Boot 3+), security rules are configured as a SecurityFilterChain bean — a plain @Bean method, not a subclassed configuration class:
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/public/**").permitAll()
.requestMatchers("/api/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
)
.formLogin(Customizer.withDefaults())
.csrf(csrf -> csrf.disable()); // acceptable for a stateless JSON API; keep enabled for form-based apps
return http.build();
}
}
.authorizeHttpRequests(...)declares which URL patterns require what — evaluated top to bottom, first match wins, so more specific rules must come before more general ones (/api/admin/**before the catch-allanyRequest())..formLogin(...)enables Spring Security's built-in login form (fine for a server-rendered app; a stateless API typically replaces this with token-based authentication instead — covered later in this track)..requestMatchers("/api/public/**").permitAll()explicitly allows unauthenticated access to a specific path pattern.
A note on legacy code: older Spring Security tutorials configure security by extending WebSecurityConfigurerAdapter and overriding configure(HttpSecurity http). That class was deprecated in Spring Security 5.7 and removed entirely in Spring Security 6 — if you see it in an example, it's for a version prior to what ships with current Spring Boot 3.x, and won't compile against it. The SecurityFilterChain @Bean approach shown above is the only supported style going forward.
Common mistakes
- Copying
WebSecurityConfigurerAdapter-based examples into a current Spring Boot 3.x project — the class doesn't exist anymore, and the fix is to express the same rules as aSecurityFilterChainbean. - Ordering
authorizeHttpRequestsrules so that a broad pattern (anyRequest().authenticated()) appears before a more specific exception (/api/public/**) — since matching is first-match-wins, the broad rule shadows the specific one and the "public" path never actually becomes public. - Disabling CSRF protection reflexively on every project without considering whether the application is actually stateless — CSRF matters for cookie/session-based browser apps and should stay enabled there; it's safe to disable only for genuinely stateless, token-authenticated APIs with no session cookies involved.
- Assuming Spring Security is "just for login forms" — its authorization model applies equally to JSON APIs, service-to-service calls, and method-level checks, independent of how authentication itself happens.