Understanding Authentication in Spring Security
Under the hood it's five small collaborators, each doing one job — and none of them quite where beginners expect.
Authentication answers one question: are you who you claim to be? In Spring Security this happens inside the filter chain, before your controller runs, and the answer — once found — gets stashed somewhere the rest of your app can read. That's the whole arc: prove identity, then record it. Everything else is a matter of which object does which part, and the names are honestly half the difficulty.
The cast of characters
Spring Security breaks authentication into small, single-purpose pieces on purpose. The AuthenticationManager is the front door — you hand it a set of credentials and it says yes or no. In practice it's a ProviderManager that delegates to one or more AuthenticationProviders, each of which knows how to check one kind of credential. The common one, DaoAuthenticationProvider, checks a username and password against a data store.
To do that it leans on two collaborators. UserDetailsService answers "does a user by this name exist, and what's their stored password and roles?" — it loads a UserDetails object, usually from your database. PasswordEncoder then checks the submitted password against the stored hash. Notice the split: one component finds the user, a different one verifies the secret. That separation is exactly why you can swap your user store without touching your password logic, and change your hashing without touching your lookup.
Why passwords are never compared directly
The PasswordEncoder deserves a moment, because it enforces a rule people get wrong. You never store a password; you store a hash of it. When someone logs in, you hash what they typed and compare hashes — the original is never persisted, and a leaked database doesn't hand out plaintext passwords. Spring Security assumes this by default and will actively complain if you try to store raw credentials. That friction is the framework protecting you from yourself, and it's worth being grateful for.
Where the result lives: the SecurityContext
Once a provider says "yes, this is them," the authenticated identity is placed in the SecurityContext, held by the SecurityContextHolder. This is the part beginners miss. Authentication isn't just a gate that swings open; it leaves a record. For the rest of that request, any code can ask "who's calling?" and get the answer from the context. When you see a controller pull the current user seemingly out of thin air, this is where it came from — a filter put it there earlier in the chain.
The mental model that makes it click
Picture a request arriving with credentials. A filter catches it and hands them to the AuthenticationManager. That delegates to a provider, which asks the UserDetailsService to find the user and the PasswordEncoder to verify the secret. If both agree, the resulting identity goes into the SecurityContext, and the request continues — now authenticated. Five components, one hand-off each.
It looks like a lot of indirection for "check a password," and it is. But every seam is a place you can substitute your own logic: a different user store, a different credential type, an entirely different notion of identity — tokens, an external provider, whatever you need. That's the trade Spring Security makes everywhere: more moving parts than you'd write for one app, in exchange for being able to change any single part without rewriting the others. Once you can name the five pieces and say what each does, authentication stops being a black box and becomes a pipeline you can actually debug.






