Spring Security

Master Spring Security — the comprehensive security framework for authentication, authorization, and protection against common web vulnerabilities in Spring Boot applications.

Last reviewed: January 2025

·Spring Boot 2.7Spring Boot 3.xSpring Security 6
✅
Quick Understanding: Spring Security is a filter chain that intercepts every HTTP request before it reaches your controllers. It checks if the request is authenticated (who are you?) and authorized (are you allowed?). Adding spring-boot-starter-security auto-configures basic security. You customize it by defining a SecurityFilterChain bean.

🔍 What Is It?

Spring Security is a powerful, customizable authentication and authorization framework.

Core concepts:

  • →Authentication — verifying identity (who are you?)
  • →Authorization — verifying permissions (what can you do?)
  • →SecurityFilterChain — the chain of filters that process every request
  • →SecurityContext — holds the current user's authentication
  • →UserDetails — the user object Spring Security works with

What Spring Security provides out of the box:

  • →HTTP Basic authentication
  • →Form login
  • →CSRF protection
  • →Session management
  • →Password encoding
  • →Method-level security (@PreAuthorize)
  • →OAuth2 / OpenID Connect
  • →CORS configuration
  • →Security headers (X-Frame-Options, X-XSS-Protection, etc.)

🎯 Why Do We Need It?

Without Spring Security, every endpoint is publicly accessible. You'd need to manually check authentication in every controller method — error-prone and inconsistent. Spring Security centralizes all security concerns in one place, applying them consistently to every request.

🏗️ What Happens in Real Projects?

In enterprise Spring Boot APIs:

  • →JWT-based stateless authentication (most common for REST APIs)
  • →OAuth2/OIDC integration with identity providers (Keycloak, Auth0, Okta)
  • →Role-based access control (RBAC) with @PreAuthorize
  • →API key authentication for service-to-service calls
  • →Basic auth for internal admin endpoints

⚙️ How It Works

Security Filter Chain:

Spring Security installs a chain of servlet filters. Every HTTP request passes through them before reaching your controllers:

  1. SecurityContextPersistenceFilter — loads security context
  2. UsernamePasswordAuthenticationFilter — handles form login
  3. BasicAuthenticationFilter — handles HTTP Basic auth
  4. BearerTokenAuthenticationFilter — handles JWT tokens
  5. ExceptionTranslationFilter — converts security exceptions to HTTP responses
  6. FilterSecurityInterceptor — enforces authorization rules

You customize which filters are active and how they behave by defining a SecurityFilterChain bean.

💻 Example

Spring Security Configuration — Modern SecurityFilterChain configuration for a REST API.

java
@Configuration
@EnableWebSecurity
@EnableMethodSecurity  // Enables @PreAuthorize, @PostAuthorize
public class SecurityConfig {

    private final JwtAuthenticationFilter jwtAuthFilter;
    private final UserDetailsService userDetailsService;

    public SecurityConfig(JwtAuthenticationFilter jwtAuthFilter,
                           UserDetailsService userDetailsService) {
        this.jwtAuthFilter = jwtAuthFilter;
        this.userDetailsService = userDetailsService;
    }

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        return http
            // Disable CSRF for stateless REST APIs
            .csrf(csrf -> csrf.disable())

            // CORS configuration
            .cors(cors -> cors.configurationSource(corsConfigurationSource()))

            // Session management — stateless for JWT
            .sessionManagement(session ->
                session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))

            // Authorization rules
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/api/v1/auth/**").permitAll()     // public
                .requestMatchers("/actuator/health").permitAll()    // health check
                .requestMatchers("/api/v1/admin/**").hasRole("ADMIN")
                .anyRequest().authenticated()                       // everything else requires auth
            )

            // Add JWT filter before username/password filter
            .addFilterBefore(jwtAuthFilter,
                UsernamePasswordAuthenticationFilter.class)

            // Custom exception handling
            .exceptionHandling(ex -> ex
                .authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED))
                .accessDeniedHandler((req, res, e) ->
                    res.sendError(HttpServletResponse.SC_FORBIDDEN))
            )

            .build();
    }

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

    @Bean
    public AuthenticationManager authenticationManager(
            AuthenticationConfiguration config) throws Exception {
        return config.getAuthenticationManager();
    }

    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowedOrigins(List.of("https://app.company.com"));
        config.setAllowedMethods(List.of("GET", "POST", "PUT", "PATCH", "DELETE"));
        config.setAllowedHeaders(List.of("Authorization", "Content-Type"));
        config.setAllowCredentials(true);
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/api/**", config);
        return source;
    }
}

🤔 Common Confusions

❓ Why does Spring Security return 403 instead of 401?

401 Unauthorized means "you need to authenticate." 403 Forbidden means "you're authenticated but not allowed." Spring Security returns 403 by default for unauthenticated requests to avoid leaking information about what resources exist. Configure an AuthenticationEntryPoint to return 401 for missing/invalid credentials.

❓ Should I disable CSRF for REST APIs?

Yes, for stateless JWT-based REST APIs. CSRF attacks require the browser to send cookies automatically — if you use JWT in Authorization headers (not cookies), CSRF doesn't apply. If you use session cookies or cookie-based auth, keep CSRF enabled.

❓ What changed in Spring Security 6 (Spring Boot 3.x)?

The old WebSecurityConfigurerAdapter was removed. You now define a SecurityFilterChain @Bean directly. Method chaining style changed to lambda DSL. The authorizeRequests() was replaced with authorizeHttpRequests(). These are breaking changes when migrating from Spring Boot 2.x.

🚨 Common Production Issues

CORS errors in production — configure allowed origins explicitly, never use * in production

All endpoints returning 403 after adding spring-boot-starter-security — configure permitAll() for public endpoints

SecurityContext not available in @Async methods — configure SecurityContextHolder.setStrategyName(MODE_INHERITABLETHREADLOCAL)

Actuator endpoints exposed publicly — secure /actuator with IP restriction or authentication

✅ Best Practices

Use SecurityFilterChain @Bean, not WebSecurityConfigurerAdapter (removed in Spring Security 6)

Use BCryptPasswordEncoder with strength 12 for password hashing

Secure Actuator endpoints — never expose them publicly

Use @PreAuthorize for fine-grained method security

Log security events (login success/failure, access denied) for audit trails

Test security configuration with @WithMockUser and @WithUserDetails

🏛️ Architect's Note

Senior Architect's Perspective

For enterprise applications, don't implement authentication yourself. Use an identity provider (Keycloak, Auth0, Okta, AWS Cognito) and configure Spring Security as an OAuth2 Resource Server. The IdP handles user management, MFA, social login, and token issuance. Your Spring Boot app just validates JWTs. This is the pattern used by every serious enterprise deployment.

💬 Frequently Asked Questions

How do I get the current user in a service?

Use SecurityContextHolder.getContext().getAuthentication() or inject @AuthenticationPrincipal in a controller and pass the user to the service. Avoid accessing SecurityContextHolder in services — it makes them harder to test. Pass the user as a parameter from the controller.

How do I test secured endpoints?

Use @WithMockUser(roles="ADMIN") on test methods to simulate an authenticated user. Use @WithUserDetails("username") to use a real user from your UserDetailsService. Use MockMvc with .with(user("admin").roles("ADMIN")) for request-level security. Add spring-security-test dependency.

🎯 Key Takeaways

1Spring Security is a filter chain that processes every request before controllers
2Define a SecurityFilterChain @Bean to customize security — WebSecurityConfigurerAdapter is removed
3Use BCryptPasswordEncoder with strength 10-12 for passwords
4@EnableMethodSecurity enables @PreAuthorize for fine-grained access control
5Disable CSRF for stateless JWT-based REST APIs
6Use an identity provider (Keycloak, Auth0) for enterprise authentication

🎯 Practice this topic in interviews

↗ Where this topic fits

Sources

Sources / References

Use these Spring Boot references to verify the concept in official docs and continue into the most relevant SplashRide pages.

Related SplashRide pages

SplashRide Spring Boot learning path

Browse the full Spring Boot topic collection and learning order on SplashRide.

Spring Boot Interview Prep

Practice Spring Boot interview questions and scenario-based answers.

Backend Engineer Career Path

A structured journey through Core Java, Spring Boot, production backend engineering, and interview preparation.

Full Stack Java Developer Career Path

A guided route through frontend foundations, Java backend engineering, delivery workflows, and interview readiness.

Authentication

Implement authentication in Spring Boot — UserDetailsService, AuthenticationManager, login endpoints, and the complete authentication flow for enterprise REST APIs.

Authorization

Implement role-based and permission-based authorization in Spring Boot using @PreAuthorize, @PostAuthorize, URL-based security, and custom authorization logic.