Appearance
Spring Boot Security Part 1 | Architecture and Setup
Introduction: Why Security Matters
Imagine you run a bank. Anyone who walks in cannot just open someone else's locker. First, they prove who they are (show ID — authentication). Then the guard checks what they are allowed to do (can they access vault A or just vault B — authorization). Without these two guards, anyone could do anything.
Your Spring Boot application is that bank. Every API endpoint is a locker room. Spring Security is the complete guard system that stands between every incoming HTTP request and your application resources.
Understanding Spring Security architecture first is critical. It is a framework that many developers use without fully understanding how it works internally, which leads to incorrect configurations, security holes, and debugging nightmares. This chapter lays the foundation.
What is Spring Security?
Spring Security is a powerful and highly customizable authentication and authorization framework for Java applications. It integrates seamlessly with Spring Boot and provides:
- Protection against common attacks: CSRF, session fixation, clickjacking
- Authentication: verifying the identity of users
- Authorization: controlling what an authenticated user can access
- Integration with various auth mechanisms: form login, basic auth, JWT, OAuth2
The key phrase to internalize is: Spring Security works as a chain of filters. Every HTTP request passes through this filter chain before it ever reaches your controller.
The Big Picture: Filter Chain Architecture
When a browser or client sends an HTTP request to your Spring Boot application, the request does not jump directly into your controller. It passes through a series of security filters first.
Client Request
|
v
[DelegatingFilterProxy]
|
v
[FilterChainProxy]
|
v
[SecurityFilterChain]
| (chain of filters)
|-- UsernamePasswordAuthenticationFilter
|-- BasicAuthenticationFilter
|-- BearerTokenAuthenticationFilter
|-- ExceptionTranslationFilter
|-- AuthorizationFilter
|
v
[DispatcherServlet]
|
v
[Your Controller]DelegatingFilterProxy
Spring registers a single standard Java Servlet filter called DelegatingFilterProxy with the Servlet container. Its job is to delegate the filtering work to a Spring bean named springSecurityFilterChain.
FilterChainProxy
The springSecurityFilterChain bean is of type FilterChainProxy. It holds multiple SecurityFilterChain instances and decides which chain handles a particular request.
SecurityFilterChain
Each SecurityFilterChain contains an ordered list of security filters. The filters run in a specific order, and each filter has a focused responsibility.
Key Filters in the Chain
UsernamePasswordAuthenticationFilter
This filter intercepts POST requests to /login. It extracts the username and password from the request form data, creates an Authentication object (UsernamePasswordAuthenticationToken), and delegates to the AuthenticationManager to authenticate it.
BasicAuthenticationFilter
This filter reads the Authorization header looking for the Basic scheme. When found, it decodes the Base64 encoded username:password and authenticates the user.
BearerTokenAuthenticationFilter (OAuth2/JWT)
This filter reads the Authorization header looking for a Bearer token and delegates to appropriate token validators.
ExceptionTranslationFilter
This filter handles two specific exceptions:
AuthenticationException→ redirects to the login page or returns 401AccessDeniedException→ returns 403
AuthorizationFilter
This is the last security filter. It checks whether the currently authenticated user has the required permissions to access the requested resource.
Core Concepts: Authentication vs Authorization
Authentication
Authentication answers the question: "Who are you?"
When a user submits their username and password:
- Spring Security intercepts the request
- It loads the user from storage (database, memory, LDAP)
- It compares the provided password with the stored (hashed) password
- If they match, the user is considered authenticated
The authenticated user information is stored in the SecurityContextHolder.
java
// The SecurityContext holds authentication info for the current thread
SecurityContext context = SecurityContextHolder.getContext();
Authentication authentication = context.getAuthentication();
String username = authentication.getName();
Object principal = authentication.getPrincipal();
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();Authorization
Authorization answers the question: "What are you allowed to do?"
After authentication, Spring Security checks whether the user has the required role or permission to access a resource. Roles are prefixed with ROLE_ by convention.
java
// Restricting access by role in security config
http.authorizeHttpRequests(auth -> auth
.requestMatchers("/admin/**").hasRole("ADMIN")
.requestMatchers("/user/**").hasRole("USER")
.anyRequest().authenticated()
);The Authentication Process in Depth
Here is the step by step flow when a user submits credentials:
1. Request arrives at UsernamePasswordAuthenticationFilter
2. Filter creates UsernamePasswordAuthenticationToken (not yet authenticated)
3. Token is passed to AuthenticationManager (ProviderManager)
4. ProviderManager delegates to one or more AuthenticationProvider instances
5. DaoAuthenticationProvider calls UserDetailsService.loadUserByUsername()
6. UserDetailsService returns UserDetails (with username, password, roles)
7. DaoAuthenticationProvider compares passwords using PasswordEncoder
8. If valid → returns fully authenticated Authentication object
9. Authentication is stored in SecurityContextHolder
10. SecurityContextPersistenceFilter saves context to session (for stateful)AuthenticationManager (ProviderManager)
|
|--> AuthenticationProvider 1 (DaoAuthenticationProvider)
| |
| |--> UserDetailsService
| | |
| | --> loadUserByUsername(username) -> UserDetails
| |
| |--> PasswordEncoder.matches(raw, encoded)
|
|--> AuthenticationProvider 2 (optional, e.g. LDAP)Setting Up Spring Security in Spring Boot
Step 1: Add the Dependency
xml
<!-- pom.xml -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>As soon as you add this dependency and restart the application, Spring Boot auto configures security. All endpoints are secured by default. A default user is created with username user and a random password printed in the console logs.
Using generated security password: 3a5c8d21-fd99-4b12-a87e-0123456789abStep 2: Create a Security Configuration Class
java
package com.example.security.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
// Public endpoints
.requestMatchers("/public/**", "/health").permitAll()
// Admin only endpoints
.requestMatchers("/admin/**").hasRole("ADMIN")
// All other requests need authentication
.anyRequest().authenticated()
)
.formLogin(form -> form
.loginPage("/login")
.permitAll()
)
.logout(logout -> logout
.permitAll()
);
return http.build();
}
}@EnableWebSecurity enables Spring Security's web security support and provides the Spring MVC integration.
Step 3: Understand the Default Auto Configuration
Without any configuration class, Spring Boot applies SpringBootWebSecurityConfiguration which:
- Enables form login
- Enables HTTP basic authentication
- Protects all endpoints
- Creates a default user
Once you define your own SecurityFilterChain bean, the auto configuration backs off and your configuration takes full control.
The SecurityContextHolder
The SecurityContextHolder is the most fundamental piece of Spring Security. It stores the security context for the current thread. Think of it as a thread local storage that holds the current user's authentication details.
java
// Accessing current user anywhere in your code
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth != null && auth.isAuthenticated()) {
String username = auth.getName();
// Get authorities (roles)
auth.getAuthorities().forEach(a -> System.out.println(a.getAuthority()));
}The context is populated after successful authentication and cleared after the request completes (or after logout).
UserDetails and UserDetailsService
UserDetails is the interface that represents a user in Spring Security's world.
java
public interface UserDetails extends Serializable {
Collection<? extends GrantedAuthority> getAuthorities();
String getPassword();
String getUsername();
boolean isAccountNonExpired();
boolean isAccountNonLocked();
boolean isCredentialsNonExpired();
boolean isEnabled();
}UserDetailsService is the interface Spring Security calls to load user data.
java
public interface UserDetailsService {
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
}You implement this interface and return a UserDetails object that Spring Security then uses for authentication and authorization.
PasswordEncoder
Never store passwords in plain text. Spring Security requires a PasswordEncoder bean to encode and verify passwords.
java
@Bean
public PasswordEncoder passwordEncoder() {
// BCrypt is the industry standard - strong, slow, and salted
return new BCryptPasswordEncoder();
}BCryptPasswordEncoder uses the BCrypt hashing function. The same password hashed twice produces different hashes (due to random salt), but matches() correctly verifies them.
java
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
String encoded = encoder.encode("mypassword");
// encoded looks like: $2a$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy
boolean matches = encoder.matches("mypassword", encoded); // true
boolean wrong = encoder.matches("wrongpass", encoded); // falseAuthentication Methods Overview
Spring Security supports multiple authentication methods. Each subsequent chapter covers one in depth:
| Method | Stateful/Stateless | Use Case |
|---|---|---|
| Form Login | Stateful (session) | Traditional web apps |
| Basic Auth | Stateless | Simple API access, internal tools |
| JWT | Stateless | REST APIs, mobile backends |
| OAuth2 | Stateless | Social login, third party integrations |
Summary
- Spring Security is a filter chain that sits between incoming HTTP requests and your application
- The key components are:
DelegatingFilterProxy,FilterChainProxy,SecurityFilterChain, and individual filters - Authentication (who are you?) precedes authorization (what can you do?)
- The
AuthenticationManagerdelegates toAuthenticationProviderinstances, which useUserDetailsServiceto load users SecurityContextHolderstores the currently authenticated user's context- Always use a
PasswordEncoder(BCrypt recommended) — never store plain text passwords - Adding the
spring-boot-starter-securitydependency immediately secures all endpoints
Interview Questions
Q1: What is the difference between authentication and authorization in Spring Security?
Authentication verifies the identity of the user (who are you?). Authorization determines what resources the authenticated user can access (what are you allowed to do?). Authentication always happens before authorization.
Q2: How does the Spring Security filter chain work?
Spring Security registers a DelegatingFilterProxy with the Servlet container. This proxy delegates to FilterChainProxy (the springSecurityFilterChain bean), which contains one or more SecurityFilterChain instances. Each chain has an ordered list of security filters. Every HTTP request passes through these filters before reaching the controller.
Q3: What is the role of SecurityContextHolder?
SecurityContextHolder stores the SecurityContext for the current thread. The SecurityContext holds the Authentication object representing the currently authenticated user. It uses a ThreadLocal strategy by default, meaning each thread has its own context. It is populated after successful authentication and cleared after request completion.
Q4: What is DaoAuthenticationProvider and how does it work?
DaoAuthenticationProvider is an implementation of AuthenticationProvider that authenticates users using a UserDetailsService and a PasswordEncoder. It calls loadUserByUsername() to fetch user details, then uses the PasswordEncoder to compare the submitted password with the stored encoded password.
Q5: Why do we need @EnableWebSecurity?
@EnableWebSecurity enables Spring Security's web security support. It imports the WebSecurityConfiguration and SpringWebMvcImportSelector configurations. Without it, the security filter chain is not properly configured. In Spring Boot, it is often implied but explicitly declaring it makes your intent clear.
Q6: What happens when you add spring-boot-starter-security to your project?
Spring Boot auto configures security for all endpoints. A default user named user is created with a random UUID password logged to the console. All endpoints except the login page require authentication. Your custom SecurityFilterChain bean overrides this auto configuration.
Q7: What is the difference between permitAll() and anonymous()?
permitAll() allows all users (authenticated or not) to access a resource. anonymous() specifically allows the anonymous authentication token to access resources. In practice, permitAll() is more commonly used and is a superset — it allows access regardless of authentication status.
Q8: What is the order of filters in Spring Security's default chain?
The key filters in order include: SecurityContextPersistenceFilter, UsernamePasswordAuthenticationFilter, BasicAuthenticationFilter, ExceptionTranslationFilter, and AuthorizationFilter. The exact order matters because later filters depend on earlier ones having set up the security context.