Spring Security is a powerful, highly customisable authentication and authorisation framework. It integrates with Spring Boot via auto-configuration and supports HTTP Basic, form login, JWT, OAuth2, SAML, and more. Understanding the security filter chain is key to configuring and debugging Spring Security correctly.

Key Points

  • Security Filter Chain: Spring Security installs a chain of servlet filters — each request passes through them in order
  • DelegatingFilterProxy → FilterChainProxy → SecurityFilterChain: the entry points from servlet container to Spring Security
  • Authentication: verifying who you are — UsernamePasswordAuthenticationToken, JwtAuthenticationToken
  • AuthenticationManager → AuthenticationProvider: pluggable authentication — DaoAuthenticationProvider for DB, JwtDecoder for tokens
  • SecurityContextHolder: ThreadLocal that holds the Authentication for the current request — clear after use
  • Authorization: what you can do — @PreAuthorize("hasRole('ADMIN')"), @Secured, or HttpSecurity.authorizeHttpRequests()
  • JWT flow: client sends Bearer token → JwtAuthenticationFilter extracts and validates → Spring sets Authentication in context
  • OAuth2 Resource Server: spring-boot-starter-oauth2-resource-server + .oauth2ResourceServer(oauth2 -> oauth2.jwt()) validates JWTs automatically
  • CSRF: enabled by default for browser clients; disable for stateless REST APIs (JWT) — http.csrf(AbstractHttpConfigurer::disable)

Spring Security: stateless JWT config, oauth2ResourceServer, @PreAuthorize method security, BCrypt password encoder

@Configuration
@EnableWebSecurity
@EnableMethodSecurity          // enables @PreAuthorize
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        return http
            .csrf(AbstractHttpConfigurer::disable)              // stateless API
            .sessionManagement(s -> s.sessionCreationPolicy(STATELESS))
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/api/auth/**").permitAll()    // public
                .requestMatchers("/api/admin/**").hasRole("ADMIN")
                .anyRequest().authenticated()
            )
            .oauth2ResourceServer(oauth2 -> oauth2
                .jwt(jwt -> jwt.jwtAuthenticationConverter(jwtConverter()))
            )
            .build();
    }

    // Extract roles from JWT claim "roles"
    @Bean
    public JwtAuthenticationConverter jwtConverter() {
        var converter = new JwtGrantedAuthoritiesConverter();
        converter.setAuthoritiesClaimName("roles");
        converter.setAuthorityPrefix("ROLE_");
        var jwtConverter = new JwtAuthenticationConverter();
        jwtConverter.setJwtGrantedAuthoritiesConverter(converter);
        return jwtConverter;
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder(12);
    }
}

// Method-level security
@Service
public class ReportService {
    @PreAuthorize("hasRole('ADMIN') or #userId == authentication.name")
    public Report getReport(String userId) { ... }

    @PreAuthorize("hasAuthority('report:write')")
    public void publishReport(Report r) { ... }
}

// Custom JWT claim extraction
@Component
public class JwtService {
    public String generateToken(UserDetails user) {
        return Jwts.builder()
            .subject(user.getUsername())
            .claim("roles", user.getAuthorities().stream().map(GrantedAuthority::getAuthority).toList())
            .issuedAt(new Date())
            .expiration(new Date(System.currentTimeMillis() + 86_400_000))
            .signWith(signingKey)
            .compact();
    }
}

Real-World Example

The most common Spring Security mistake is accidentally exposing all endpoints. The default HttpSecurity in Spring Boot 3 requires authentication for all endpoints — good. But adding .authorizeHttpRequests().anyRequest().permitAll() during development and forgetting to remove it is a critical security gap. Always review SecurityFilterChain configuration before deployment, and write integration tests for secured endpoints.