Spring Framework Tutorial 0/14 lessons ~6 min read Lesson 13

    Spring Security Basics

    Spring Security handles authentication (who are you?) and authorization (what may you do?).

    Course progress0%
    Focus
    8 guided sections
    Practice signal
    Examples included
    Career prep
    Foundation builder

    Introduction

    Spring Security handles authentication (who are you?) and authorization (what may you do?). It works as a chain of filters that sit in front of your controllers and decide whether each request continues.

    It has a reputation for being hard, but the mental model is small: a filter chain, an authentication strategy, and a set of access rules. Once those three click, the rest is configuration.

    Understanding the topic

    The mental model:

    • Every request walks a configurable filter chain.
    • Filters extract credentials (form login, JWT, OAuth2, API key…) and build an Authentication object.
    • An AuthenticationManager validates it against a UserDetailsService (or external provider).
    • An access decision checks roles or expressions before the controller runs.
    • If everything passes, the request reaches your controller with the user available via SecurityContextHolder.

    Syntax reference

    A minimal HTTP security setup:

    java
    @Configuration
    @EnableWebSecurity
    public class SecurityConfig {
    @Bean
    SecurityFilterChain api(HttpSecurity http) throws Exception {
    return http
    .authorizeHttpRequests(reg -> reg
    .requestMatchers("/api/public/**").permitAll()
    .requestMatchers("/api/admin/**").hasRole("ADMIN")
    .anyRequest().authenticated())
    .httpBasic(Customizer.withDefaults())
    .csrf(csrf -> csrf.disable()) // safe for stateless JSON APIs
    .build();
    }
    @Bean
    PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
    }
    }

    Informative example

    Method-level rules for domain-aware authorization:

    java
    @Service
    class DocumentService {
    private final DocumentRepository repo;
    @PreAuthorize("hasRole('ADMIN') or #ownerId == authentication.name")
    public List<Document> listFor(String ownerId) {
    return repo.findByOwner(ownerId);
    }
    @PostAuthorize("returnObject.ownerId == authentication.name")
    public Document get(Long id) {
    return repo.findById(id).orElseThrow();
    }
    }

    Method-level rules are perfect when the authorization decision depends on the data itself, not just on the URL.

    Real-world use

    Modern Spring apps almost always pair Spring Security with either JWTs (for stateless APIs consumed by SPAs and mobile clients) or OAuth2 / OIDC (for delegated authentication via Google, GitHub, Keycloak, etc.). The filter chain stays the same — only the filter that extracts credentials changes.

    Best practices

    • Always design authorization alongside authentication — never bolt it on later.
    • Use method-level @PreAuthorize for rules that depend on the domain object.
    • Never store passwords in plain text — always go through a PasswordEncoder.
    • Disable CSRF only for stateless APIs; keep it enabled for server-rendered pages.

    Common mistakes

    • Permitting a path too broadly (/api/** instead of /api/public/**) and exposing private endpoints.
    • Forgetting to encode passwords before saving — the next login will silently fail.
    • Putting business logic inside a filter — keep filters about authentication, not domain rules.

    Hands-on exercise

    Build it: secure a tiny app with two endpoints — /api/public/ping (anyone) and /api/admin/stats (only users with role ADMIN). Add an in-memory user store with one regular user and one admin. Test both endpoints with HTTP basic auth and confirm the 401/403 behaviour.

    Ready to mark this lesson complete?Track your journey across the entire course.