Spring Security Fundamentals

Introduction to Spring Security

18 min Lesson 1 of 13

Introduction to Spring Security

Every web application eventually faces the same question: who is allowed in, and what are they allowed to do? Spring Security is the standard answer for the Spring ecosystem. It is a powerful, highly customisable framework that handles authentication (proving who you are) and authorisation (deciding what you may access), while also providing defences against a catalogue of common web-layer attacks.

This lesson covers what Spring Security actually protects against, the mental model you need before writing any configuration, and exactly how the library integrates with a Spring Boot 3 application without any extra plumbing code.

What Threats Does Spring Security Address?

Before configuring anything it is worth being concrete about the attacks Spring Security guards you from by default.

  • Cross-Site Request Forgery (CSRF): A malicious page tricks a user's browser into firing a state-changing request (e.g. a bank transfer) to your application, riding the user's session cookie. Spring Security enables CSRF token protection automatically for every state-changing HTTP method (POST, PUT, DELETE, PATCH).
  • Session fixation: An attacker forces a known session ID onto a victim before login. Spring Security migrates (or invalidates) the session on every successful authentication, making pre-issued session IDs useless.
  • Clickjacking: Embedding your site in a hidden iframe lets an attacker overlay fake UI on top. Spring Security emits an X-Frame-Options: DENY header by default.
  • Content sniffing & XSS vectors: Headers such as X-Content-Type-Options and the Content-Security-Policy support built into HeadersConfigurer mitigate browser-level attacks.
  • Credential brute-forcing: By enforcing authentication on every sensitive route and plugging into password-encoding strategies, Spring Security eliminates the easy wins for automated credential-stuffing attacks.
Default-secure stance: If you add Spring Security to a project and configure nothing else, it locks every URL behind HTTP Basic authentication and generates a random password printed to the console. The framework assumes closed-by-default, which is a safer starting point than open-by-default.

The Core Concepts at a Glance

Spring Security is built around three abstractions that repeat throughout every feature you will learn:

  1. Principal — the currently authenticated entity (a user, a service account, an OAuth2 client). Stored in a thread-local SecurityContext.
  2. Authentication — an object that represents both the credentials submitted during login and, once verified, the confirmed identity and its authorities. The interface is org.springframework.security.core.Authentication.
  3. GrantedAuthority — a permission or role attached to the authenticated principal (e.g. ROLE_ADMIN, orders:read).

These three objects flow through every request and are what your authorisation rules ultimately interrogate.

How Spring Security Plugs into a Spring Boot App

Spring Security integrates at the Servlet filter layer. It registers a single entry-point filter — DelegatingFilterProxy — with the servlet container, which in turn delegates to a Spring-managed FilterChainProxy. That proxy holds an ordered list of security filter chains, each of which is a sequence of specialised filters (CSRF, session management, authentication, authorisation, and more).

The practical implication: Spring Security operates before any @Controller or @RestController ever runs. A request that fails authentication or authorisation is rejected at the filter level and never reaches your application code.

// Spring Boot 3 — build.gradle (Kotlin DSL) dependencies { implementation("org.springframework.boot:spring-boot-starter-security") implementation("org.springframework.boot:spring-boot-starter-web") }

That single starter dependency is all you need. Spring Boot's auto-configuration detects it on the classpath and:

  • Creates a default UserDetailsService with an in-memory user (user / random password).
  • Registers the FilterChainProxy with the servlet container.
  • Enables HTTP Basic and form-login on every URL.
  • Switches on CSRF protection, security headers, and session fixation protection.
Check the auto-configuration report: Run your app with --debug and search for SecurityAutoConfiguration in the output. You will see exactly which beans were created and which conditions were matched. This is invaluable when something does not behave as expected.

A Minimal Verified Example

Let us write the smallest possible Spring Boot 3 application that demonstrates Spring Security in action. Nothing is configured — we rely entirely on auto-configuration.

package com.example.security; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class SecurityDemoApplication { public static void main(String[] args) { SpringApplication.run(SecurityDemoApplication.class, args); } }
package com.example.security; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class HelloController { @GetMapping("/hello") public String hello() { return "Hello, authenticated user!"; } }

Start the application. In the console you will see a line similar to:

Using generated security password: 3fa16c7d-9e02-4b8f-8e16-4c1f2d3a7b90 This generated password is for development use only. Your security configuration must be updated before running your application in production.

A GET /hello request without credentials receives a 401 Unauthorized response with a WWW-Authenticate: Basic header. Supply the username user and the generated password via HTTP Basic and the request succeeds. Spring Security intercepted the request, verified the credentials, stored the resulting Authentication in the SecurityContext, and then allowed the request to continue to the controller.

The SecurityContext and the Current User

Once a request is authenticated, the verified Authentication object lives in a thread-local SecurityContextHolder. Any code running on that thread — including your controllers and services — can read it:

import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class MeController { @GetMapping("/me") public String currentUser() { Authentication auth = SecurityContextHolder.getContext().getAuthentication(); return "Logged in as: " + auth.getName() + " | Authorities: " + auth.getAuthorities(); } }

In practice you will inject the authentication as a method parameter using @AuthenticationPrincipal or via Spring MVC's parameter injection, which is cleaner and more testable. But the raw SecurityContextHolder call makes the underlying model explicit.

Never share a SecurityContext across threads manually. The default SecurityContextHolder strategy (MODE_THREADLOCAL) does not propagate the context to child threads or async tasks. If you use @Async, CompletableFuture, or virtual threads, switch to MODE_INHERITABLETHREADLOCAL or use Spring Security's DelegatingSecurityContextExecutor wrapper — otherwise the security context will be null on the worker thread and your authorisation checks will silently fail.

Where This Tutorial Goes from Here

With the big picture in place, the remaining lessons build on it systematically. Lesson 2 dissects the Security Filter Chain so you understand what fires on every request. Lesson 3 draws the precise line between authentication and authorisation. Lesson 4 shows how to replace auto-configuration with a custom SecurityFilterChain bean — the idiomatic approach in Spring Security 6. Lessons 5 and 6 cover loading users and encoding passwords. The final lessons tackle authorisation rules, method-level security, and assembling a complete secured web application.

Summary

Spring Security integrates at the servlet filter layer, running before any controller code. Adding the spring-boot-starter-security dependency activates a closed-by-default security posture that protects against CSRF, session fixation, clickjacking, and content-sniffing attacks out of the box. The three core abstractions — Principal, Authentication, and GrantedAuthority — flow through every request and underpin every feature you will configure in the lessons ahead. The verified Authentication is stored in a thread-local SecurityContextHolder and is accessible to all code on the request thread.