Skip to content

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 401
  • AccessDeniedException → 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:

  1. Spring Security intercepts the request
  2. It loads the user from storage (database, memory, LDAP)
  3. It compares the provided password with the stored (hashed) password
  4. 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-0123456789ab

Step 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); // false

Authentication Methods Overview

Spring Security supports multiple authentication methods. Each subsequent chapter covers one in depth:

MethodStateful/StatelessUse Case
Form LoginStateful (session)Traditional web apps
Basic AuthStatelessSimple API access, internal tools
JWTStatelessREST APIs, mobile backends
OAuth2StatelessSocial 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 AuthenticationManager delegates to AuthenticationProvider instances, which use UserDetailsService to load users
  • SecurityContextHolder stores the currently authenticated user's context
  • Always use a PasswordEncoder (BCrypt recommended) — never store plain text passwords
  • Adding the spring-boot-starter-security dependency 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.