How Spring Security Works Internally

📘 Premium Read: Access my best content on Medium member-only articles — deep dives into Java, Spring Boot, Microservices, backend architecture, interview preparation, career advice, and industry-standard best practices.

✅ Some premium posts are free to read — no account needed. Follow me on Medium to stay updated and support my writing.

🎓 Top 10 Udemy Courses (Huge Discount): Explore My Udemy Courses — Learn through real-time, project-based development.

▶️ Subscribe to My YouTube Channel (172K+ subscribers): Java Guides on YouTube

 Spring Security is a robust and highly customizable framework that helps developers secure Java applications. Whether you’re building REST APIs or web apps, Spring Security gives you powerful tools to handle authentication, authorization, and user management.

But what actually happens behind the scenes when a user logs in? How does Spring know who the user is and what they can access?

In this article, we’ll break down the core components of Spring Security, understand how they work together, and walk through a real authentication flow.

What is Spring Security?

Spring Security is a part of the Spring ecosystem that focuses on providing authentication, authorization, and protection against common exploits such as CSRF, session fixation, clickjacking, etc.

It can:

  • Authenticate users (who are you?)
  • Authorize requests (what are you allowed to do?)
  • Integrate with databases, LDAP, OAuth2, JWT, etc.
In short, Spring Security is your first line of defense in any Java/Spring-based web application.

🏃‍♂️ Key Spring Security Components

Before diving into the internal flow, let’s understand the core building blocks of Spring Security. These components make up the foundation of how the security system functions.

✨ Principal

Represents the currently logged-in user.

You can get it using:

Authentication auth = SecurityContextHolder.getContext().getAuthentication();
Object principal = auth.getPrincipal();

Usually, this is a UserDetails object.


✔️ Authentication

Means confirming the identity of a user using credentials like username and password.

public interface Authentication extends Principal {
Object getCredentials();
Object getPrincipal();
boolean isAuthenticated();
}

⛔ Authorization

Defines what the authenticated user is allowed to do. For example, can the user access /admin?

Spring uses roles and authorities to enforce these rules.


🔑 GrantedAuthority

Represents a permission granted to the principal. Roles like ROLE_USER, ROLE_ADMIN are examples.

Collection<? extends GrantedAuthority> getAuthorities();

📌 AuthenticationManager

The controller of the authentication process. It decides whether authentication is successful.

It exposes the method:

Authentication authenticate(Authentication authentication) throws AuthenticationException;

Often used with in-memory users, database users, or third-party login systems.


🧱 AuthenticationProvider

It connects Spring Security with your user data source (like DB or LDAP).

You can have multiple providers for different types of logins.

boolean supports(Class<?> authentication);
Authentication authenticate(Authentication authentication);

💊 Authentication Object

Created during login, this object holds the credentials and principal.

Before login:

  • Username and password (credentials only)

After login:

  • Fully populated with user info and authorities

📄 UserDetails

A core object in Spring Security. It contains the user’s credentials and roles.

public interface UserDetails {
String getUsername();
String getPassword();
Collection<? extends GrantedAuthority> getAuthorities();
}

🔎 UserDetailsService

Used to load user data from a source (DB, memory, etc.).

You implement it like this:

@Override
public UserDetails loadUserByUsername(String username) {
return new User("ramesh", "{noop}1234", List.of(new SimpleGrantedAuthority("ROLE_USER")));
}

This is injected into DaoAuthenticationProvider.


🛡️ How Spring Security Works Internally (Authentication Flow)

Spring Security is not just about login pages and roles — under the hood, it follows a well-defined authentication flow that makes it modular, pluggable, and secure.

Let’s walk through the authentication process based on the diagram:

🔐 Step-by-Step Spring Security Authentication Flow

1️⃣ Authentication Filter

  • Entry point for any authentication request (e.g., login form, JWT token, Basic Auth).
  • Captures the user’s credentials from the request (username/password, token, etc.)
  • Creates an Authentication object with just the credentials (not yet authenticated).
  • Passes this object to the AuthenticationManager.

🧠 Example: UsernamePasswordAuthenticationFilter, BearerTokenAuthenticationFilter


2️⃣ AuthenticationManager

  • The main component that coordinates the authentication process.
  • Calls .authenticate() method on each registered AuthenticationProvider until one supports the authentication type.

📌 Think of it as a dispatcher that finds the right provider to handle the request.


3️⃣ AuthenticationProvider(s)

Each AuthenticationProvider is responsible for checking credentials for a specific type of Authentication (like username-password, LDAP, JWT).

Two key methods:

  • supports() → Can I handle this Authentication type?
  • authenticate() → Validate the credentials and return a fully authenticated Authentication object.

If successful:

  • It creates a new Authentication object with the principal (UserDetails) and roles.
  • Sends it back to AuthenticationManager.

4️⃣ UserDetailsService

Called by the AuthenticationProvider (usually DaoAuthenticationProvider).

Loads the user info by username from a database or in-memory store.

Returns a UserDetails object, which includes:

  • username
  • password (hashed)
  • roles/authorities
  • isAccountNonExpired, isEnabled, etc.

🧠 You implement this with:

@Override
public UserDetails loadUserByUsername(String username) {
return userRepository.findByUsername(username)
.map(user -> new User(user.getUsername(), user.getPassword(), List.of(new SimpleGrantedAuthority("ROLE_USER"))))
.orElseThrow(() -> new UsernameNotFoundException("User not found"));
}

5️⃣ Authentication Object (Principal)

Once verified:

  • A new Authentication object is created — this time with full principal (UserDetails) info and authorities.
  • This is marked as authenticated.

It flows back from: 

➡️ AuthenticationProvider
 ➡️ AuthenticationManager
 ➡️ AuthenticationFilter


6️⃣ SecurityContext

Finally, Spring Security stores the authenticated Authentication object in the SecurityContext, which is tied to the current thread/request.

You can access it anywhere in your code like:

Authentication auth = SecurityContextHolder.getContext().getAuthentication();
String username = auth.getName();

🧾 Summary Flow


✅ Final Thoughts

Spring Security’s internal design:

  • Is modular: plug in custom filters, providers, services
  • Is secure by default
  • Can support multiple authentication types (form login, JWT, LDAP, OAuth2)
Once you understand how AuthenticationManager and AuthenticationProvider interact, writing custom login logic or token-based auth becomes much easier.

Comments

Spring Boot 3 Paid Course Published for Free
on my Java Guides YouTube Channel

Subscribe to my YouTube Channel (165K+ subscribers):
Java Guides Channel

Top 10 My Udemy Courses with Huge Discount:
Udemy Courses - Ramesh Fadatare