Spring Security Form-Based & HTTP Basic Authentication

Learn how to implement form-based login and HTTP Basic authentication in Spring Security, with configuration examples, security hardening, and failure scenarios.

published: reading time: 27 min read author: GeekWorkBench
Quick Summary

Learn how to implement form-based login and HTTP Basic authentication in Spring Security, with configuration examples, security hardening, and failure scenarios. The guide uses practical examples to explain when to use form-based login, when to use http basic authentication and shows how to apply the ideas in a Spring Boot project.

Spring Security Form-Based & HTTP Basic Authentication

Spring Security ships with two built-in authentication mechanisms: form-based login that mimics the traditional web login page flow, and HTTP Basic, a lightweight scheme embedded directly in the HTTP headers. Both are straightforward to set up, but they behave very differently once credentials leave the browser.

This guide cuts through the documentation and gets into the practical differences. When does each approach make sense? How does the filter chain actually process credentials? Where do things go wrong in production?

When to Use Form-Based Login

Form-based login works well in traditional web applications where users interact through a browser. Session state lives server-side, the browser handles cookies naturally, and you get a branded login page with full control over the user experience.

Use form-based login when your app serves HTML from the server or when you need fine-grained control over session timeout, remember-me functionality, and CSRF handling. It integrates directly with Spring Security’s filter chain.

Skip it for SPAs that consume APIs. The redirect-to-login, process-credentials, redirect-back flow adds round-trips that become noise when your frontend manages its own routing.

When to Use HTTP Basic Authentication

HTTP Basic authentication sends credentials with every request, encoded in the Authorization header. No login page, no redirect, no session cookie. The username and password travel Base64-encoded with each request, so HTTPS is mandatory.

This scheme fits machine-to-machine communication, non-browser API clients, and situations where you want to avoid server-side session state. curl, Postman, or a backend service calling your microservices can authenticate without managing cookies or tokens.

Skip it for browser-based web apps. A native browser authentication dialog feels outdated and delivers a poor experience. Credentials also travel with every request, which increases exposure if HTTPS is misconfigured.

Authentication Flow Comparison

The filter chain handles each mechanism very differently. Here is what happens under the hood.

graph TD
    A[Client Request] --> B{Authentication Type?}
    B -->|Form Login| C[UsernamePasswordAuthenticationFilter]
    B -->|HTTP Basic| D[BasicAuthenticationFilter]
    C --> E[Login Page GET /login]
    C --> F[Credential POST /login]
    F --> G{Valid Credentials?}
    G -->|Yes| H[SecurityContext Created]
    G -->|No| I[AuthenticationFailureHandler]
    D --> J{HTTP Header Present?}
    J -->|Yes| K[Extract & Decode Credentials]
    J -->|No| L[WWW-Authenticate Header Sent]
    K --> M{Valid Credentials?}
    M -->|Yes| N[SecurityContext Created]
    M -->|No| O[401 Unauthorized]
    H --> P[Session Created]
    P --> Q[Redirect to Saved URL]
    N --> R[No Session Created]
    R --> S[Response with Resource]
    I --> T[Redirect to /login?error]

Form login creates a session after successful authentication and stores the SecurityContext in the HTTP session. Subsequent requests use the session cookie to restore authentication without re-entering credentials. HTTP Basic reconstructs authentication from the request header on each call, meaning there is no session to manage or timeout.

Failure Scenarios

Both mechanisms fail differently. Knowing what happens when things break is essential for building proper monitoring and user feedback.

Session Fixation

Session fixation attacks exploit the way sessions are created and transferred. An attacker creates a session, tricks a victim into logging in using that same session identifier, and then hijacks the authenticated session. Spring Security protects against this by creating a new session ID after authentication when using form login. HTTP Basic does not use sessions at all, sidestepping this class of attack entirely. With form login, keep sessionFixation().migrateSession() enabled, which is the default.

CSRF on Login

Cross-Site Request Forgery on login is an often-overlooked attack vector. An attacker can craft a login request from a victim’s browser to your site using the victim’s credentials. If CSRF protection is absent from the login form, the victim ends up authenticated as the attacker, potentially exposing data to the wrong session. Spring Security applies CSRF tokens to state-changing operations by default, including form submissions. The login form must include the CSRF token in a hidden field. The token value comes from the CsrfToken request attribute.

Brute Force Attacks

Both mechanisms are vulnerable to credential stuffing and brute force attacks without additional protection. Failed login attempts need rate limiting, temporary account lockouts, or CAPTCHA challenges to prevent automated attacks. Spring Security does not include these protections built into the core framework, so you typically implement them as a custom AuthenticationFailureHandler or delegate to a library like Bucket4j with a Servlet filter. Account takeover protection requires tracking failed attempts by IP and username, with exponential backoff on repeated failures.

Trade-Off Comparison

Each authentication mechanism makes different trade-offs around session management, client compatibility, security posture, and operational complexity.

Aspect Form Login HTTP Basic OAuth2 Session Auth Token Auth
Credential transmission POST body, session cookie HTTP header Redirect + tokens Cookie Header/body
Session management Server-side session None Token storage Server-side Client-side
Browser compatibility Full Limited Full Full Full
API client suitability Poor Excellent Good Poor Excellent
CSRF protection needed Yes No Yes (stateful) Yes Sometimes
Mobile/native clients Requires web view Good Requires browser Poor Excellent
Logout mechanism Session invalidation N/A Token revocation Session invalidation Token blacklist
Remember-me support Built-in Not available Via refresh token Via cookie Via refresh token
HTTPS required Recommended Mandatory Recommended Recommended Recommended

Form login and session-based authentication share the same session management concerns. Token-based approaches like JWT remove server-side state but shift complexity to token validation and revocation. OAuth2 adds an authorization server, token endpoint, and client registration to your architecture.

Implementation

Setting up both mechanisms requires a Spring Security configuration class and knowing which components participate in the filter chain.

Form Login Configuration

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(authz -> authz
                .requestMatchers("/login", "/css/**", "/error").permitAll()
                .anyRequest().authenticated()
            )
            .formLogin(form -> form
                .loginPage("/login")
                .loginProcessingUrl("/login")
                .defaultSuccessUrl("/dashboard", true)
                .failureUrl("/login?error=true")
                .permitAll()
            )
            .logout(logout -> logout
                .logoutUrl("/logout")
                .logoutSuccessUrl("/login?logout=true")
                .invalidateHttpSession(true)
                .deleteCookies("JSESSIONID")
            )
            .sessionManagement(session -> session
                .sessionFixation().migrateSession()
                .maximumSessions(1)
                .maxSessionsPreventsLogin(false)
            );

        return http.build();
    }

    @Bean
    public UserDetailsService userDetailsService() {
        UserDetails user = User.builder()
            .username("user")
            .password(passwordEncoder().encode("password"))
            .roles("USER")
            .build();

        UserDetails admin = User.builder()
            .username("admin")
            .password(passwordEncoder().encode("admin123"))
            .roles("USER", "ADMIN")
            .build();

        return new InMemoryUserDetailsManager(user, admin);
    }

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

The configuration chains together several decisions. authorizeHttpRequests declares which paths are public and which require authentication. formLogin() customizes the login page URL, the processing endpoint, success handling, and failure redirect. The logout block invalidates the session and clears cookies.

HTTP Basic Configuration

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(authz -> authz
                .requestMatchers("/health", "/info").permitAll()
                .anyRequest().authenticated()
            )
            .httpBasic(Customizer.withDefaults())
            .sessionManagement(session -> session
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            );

        return http.build();
    }
}

httpBasic() activates the BasicAuthenticationFilter for all requests. SessionCreationPolicy.STATELESS tells Spring Security never to create an HTTP session, reinforcing that each request authenticates independently.

Custom Login Page

Your login page controller renders the HTML form and must include the CSRF token. Thymeleaf handles this naturally with the Spring Security dialect.

<!DOCTYPE html>
<html xmlns:th="https://www.thymeleaf.org">
  <head>
    <title>Login</title>
    <link rel="stylesheet" th:href="@{/css/login.css}" />
  </head>
  <body>
    <div class="login-container">
      <h2>Sign In</h2>

      <div th:if="${param.error}" class="error-message">
        Invalid username or password.
      </div>
      <div th:if="${param.logout}" class="logout-message">
        You have been logged out.
      </div>

      <form th:action="@{/login}" method="post">
        <input
          type="hidden"
          th:name="${_csrf.parameterName}"
          th:value="${_csrf.token}"
        />
        <div class="form-group">
          <label for="username">Username</label>
          <input type="text" id="username" name="username" autofocus required />
        </div>
        <div class="form-group">
          <label for="password">Password</label>
          <input type="password" id="password" name="password" required />
        </div>
        <button type="submit">Sign In</button>
      </form>
    </div>
  </body>
</html>

The hidden CSRF input field is critical. Without it, Spring Security rejects the POST request with a 403. The form posts to /login, handled internally by UsernamePasswordAuthenticationFilter.

Observability Checklist

Log and monitor security-relevant events to detect attacks and diagnose authentication failures in production.

  • Log failed login attempts with username, source IP, and user agent at WARN level
  • Log successful authentications at INFO level, capturing session ID for audit trails
  • Monitor account lockout state transitions in your application metrics
  • Track authentication latency to detect credential stuffing that introduces latency spikes
  • Emit events for session creation and destruction to support session hijacking detection
  • Capture the HttpServletRequest.remoteAddr for all authentication attempts, aware of proxy headers like X-Forwarded-For
  • Send alerts when failed attempts from a single IP exceed a threshold within a time window
  • Monitor for users authenticating from impossible geographic locations based on IP geolocation

Spring Security’s ApplicationEvent system publishes events like AuthenticationSuccessEvent and AuthenticationFailureBadCredentialsEvent that you can listen for and forward to your logging infrastructure.

@Component
public class AuthenticationEventListener {

    private static final Logger log = LoggerFactory.getLogger(
        AuthenticationEventListener.class);

    @EventListener
    public void onAuthenticationSuccess(AuthenticationSuccessEvent event) {
        Authentication auth = event.getAuthentication();
        HttpServletRequest request = ((ServletRequestAttributes)
            RequestContextHolder.getRequestAttributes()).getRequest();

        log.info("Authentication success for user: {} from IP: {}",
            auth.getName(),
            request.getRemoteAddr());
    }

    @EventListener
    public void onAuthenticationFailure(
            AuthenticationFailureBadCredentialsEvent event) {
        Authentication auth = event.getAuthentication();
        HttpServletRequest request = ((ServletRequestAttributes)
            RequestContextHolder.getRequestAttributes()).getRequest();

        log.warn("Failed authentication attempt for user: {} from IP: {}",
            auth.getName(),
            request.getRemoteAddr());
    }
}

Security Notes

Hardening your authentication configuration requires attention to details that live in the gap between “works” and “secure.”

Password Encoding

Never store passwords in plain text or using weak hashing algorithms like MD5 or SHA-1. BCryptPasswordEncoder applies bcrypt with a work factor that makes brute-force attacks computationally expensive. Configure it as a bean and inject it wherever password encoding is needed. If you integrate with an existing user store, DelegatingPasswordEncoder routes encoding and matching to the appropriate algorithm based on a stored prefix.

Session cookies must include the Secure flag to prevent transmission over plain HTTP, the HttpOnly flag to block JavaScript access, and the SameSite attribute to mitigate CSRF attacks. Spring Boot configures these automatically when server.servlet.session.cookie.secure=true is set. Verify your application.properties or application.yml includes this setting before deploying.

HTTPS Requirement

Both form login and HTTP Basic require HTTPS. For HTTP Basic, credentials travel in the clear without transport encryption, making HTTPS mandatory. For form login, session cookies and form data containing credentials also require encryption in transit. Configure your security filter chain to redirect HTTP to HTTPS or reject non-TLS requests entirely.

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .requiresChannel(channel -> channel
                .anyRequest().requiresSecure()
            )
            // ... rest of configuration
        ;
        return http.build();
    }
}

Common Pitfalls

The most frequent mistakes with form login and HTTP Basic are avoidable with some awareness of how the filter chain behaves.

Disabling CSRF globally. Developers sometimes disable CSRF protection to simplify testing, then leave it disabled in production. CSRF protection is essential for form login. Only disable it for stateless APIs consuming credentials in the Authorization header or for endpoints consumed by API clients that handle tokens correctly.

Not invalidating sessions on logout. The default logout behavior in Spring Security invalidates the session and clears the security context. If you customize logout and forget invalidateHttpSession(true), the session persists with the authenticated user still in the security context, giving attackers a window to hijack it.

Hardcoded credentials in configuration. Embedding usernames and passwords directly in application.properties or the security config works for demos but creates credential exposure risk in version control and deployment pipelines. Externalize credentials to a vault or environment variables, and use PasswordEncoder consistently across all authentication entry points.

Ignoring the session creation policy. Form login and HTTP Basic have opposing session requirements. Form login needs sessions. HTTP Basic benefits from stateless operation. Mixing them without explicitly setting SessionCreationPolicy can create unexpected sessions for API clients that never intended to manage session state.

Not handling the authentication entry point correctly. When an unauthenticated request hits a protected resource, Spring Security’s AuthenticationEntryPoint decides what happens. Form login redirects to the login page with a 200 response. HTTP Basic sends a WWW-Authenticate header and returns 401. If your API returns 401 but your frontend expects a redirect to login, you need a custom AuthenticationEntryPoint that handles both formats based on the Accept header.

Credential Replay Risk

HTTP Basic sends credentials with every request, encoded in the Authorization header. If an attacker intercepts the request through a man-in-the-middle attack, they can extract the Base64-encoded username and password and replay them against other endpoints. Unlike form login where credentials travel only during the login POST, HTTP Basic exposes credentials on every request. This makes HTTPS mandatory and increases the attack surface. For high-security applications, consider OAuth2 with short-lived tokens instead, where credentials never travel after initial authentication.

Header Injection in Credentials

Form login submissions and HTTP Basic credentials that contain special characters can introduce header injection vulnerabilities if the server does not sanitize input before using it in HTTP headers. While Spring Security’s filters handle this safely, custom authentication mechanisms that write username or other user data into response headers without validation can enable response splitting attacks. Always validate and encode user input before using it in any HTTP header construction.

Use this checklist when setting up form login or HTTP Basic authentication in a Spring Security application.

  • Public paths (/login, /css/**, /health) declared with permitAll()
  • All other paths require authentication with authenticated()
  • Form login configured with custom or default login page
  • Login processing URL posts to /login with CSRF token included
  • Logout handler invalidates session and clears cookies
  • Session fixation protection enabled (default, do not disable)
  • BCryptPasswordEncoder configured for credential storage
  • Failed login attempts logged with IP and username
  • HTTPS required for all authentication endpoints
  • HttpOnly and Secure flags set on session cookie
  • SessionCreationPolicy set explicitly for the authentication mechanism
  • Account lockout strategy implemented to prevent brute force
  • AuthenticationEntryPoint handles both API and browser clients correctly
  • Logout redirects to public page after session invalidation

Interview Questions

1. How does Spring Security handle form login authentication, and what filters participate in the process?

Form login flows through the UsernamePasswordAuthenticationFilter, which extracts credentials from the HTTP POST request body (or the servlet request parameters). On submission, the filter creates an UsernamePasswordAuthenticationToken and delegates to the configured AuthenticationManager.

The authentication manager queries the UserDetailsService to load user credentials and the PasswordEncoder to verify the submitted password. If verification succeeds, the filter updates the SecurityContextHolder with an authenticated token, and the SecurityContextPersistenceFilter persists it to the HTTP session.

On success, the AuthenticationSuccessHandler runs (defaulting to a redirect to the saved request URL). On failure, the AuthenticationFailureHandler runs (defaulting to a redirect back to the login page with an error parameter).

2. What is the difference between session-based and stateless authentication in Spring Security, and when would you choose each?

Session-based authentication stores the authenticated SecurityContext in the HTTP session after the first successful authentication. Subsequent requests restore context from the session cookie, meaning the application validates credentials once and relies on the session for all later requests.

Stateless authentication (used by HTTP Basic or JWT tokens) creates no session. Every request carries credentials or a token, and the server validates them on each request without consulting any server-side state.

Choose session-based authentication for browser-based web applications where users interact through forms, when you need fine-grained session management, and when logout invalidation matters. Choose stateless when building APIs consumed by multiple client types, when scaling horizontally without sticky sessions is a priority, or when machine-to-machine communication dominates.

3. What security vulnerabilities does CSRF protection mitigate in form login, and how does Spring Security implement it?

CSRF (Cross-Site Request Forgery) allows an attacker to trick a victim's browser into sending authenticated requests to your application without the victim's knowledge. For form login, a CSRF attack could force a victim to authenticate as the attacker, contaminating the victim's session with the attacker's account and giving the attacker access to data the victim did not intend to expose.

Spring Security implements CSRF protection by generating a cryptographically random token on each session and requiring that token in state-changing requests. For form login, the login form must include the token in a hidden field. The CsrfFilter validates the token on POST, PUT, DELETE, and other mutating methods. If the token is missing or mismatched, the request fails with a 403 Forbidden.

4. How do you configure HTTP Basic and form login simultaneously in Spring Security?

Add both .formLogin() and .httpBasic() to your HttpSecurity chain in the same configuration. Spring Security's filter chain applies both authentication mechanisms, checking credentials in order of the filter chain.

http
    .authorizeHttpRequests(authz -> authz
        .requestMatchers("/public/**").permitAll()
        .anyRequest().authenticated()
    )
    .formLogin(form -> form
        .loginPage("/login")
        .permitAll()
    )
    .httpBasic(Customizer.withDefaults());

The browser gets a login page redirect from form login. API clients like curl or Postman receive a WWW-Authenticate header and can authenticate via HTTP Basic. You can also differentiate behavior by checking the Accept header in a custom AuthenticationEntryPoint.

5. What is session fixation, and how does Spring Security protect against it during form login?

Session fixation attacks succeed when an attacker can force a victim to use a session ID the attacker knows before the victim authenticates. After the victim logs in, the attacker uses the same session ID to hijack the authenticated session.

Spring Security counters this by changing the session ID after authentication when using form login. The default sessionFixation().migrateSession() setting creates a new HTTP session with a fresh session ID after the user authenticates, invalidating any session ID the attacker might have known. Subsequent requests use the new session ID, and the attacker's old session ID is useless.

HTTP Basic does not use sessions, so session fixation attacks are not applicable to it by default. However, if you combine HTTP Basic with session-creating features like Spring MVC's session attributes, the same protections apply.

6. How does BCryptPasswordEncoder protect against rainbow table attacks, and why is the work factor important?

BCryptPasswordEncoder uses the BCrypt hashing algorithm, which is deliberately slow and memory-intensive. It generates a random salt for each password and applies the hash iteratively (configurable via the work factor). The work factor controls the number of iterations, directly impacting how long it takes to hash a single password.

Rainbow tables are precomputed hash tables that reverse cryptographic hashes back to plaintext passwords. BCrypt defeats them by combining a random salt with a slow hash function. Even if two users have the same password, their BCrypt hashes differ due to unique salts. The work factor makes brute-force attacks computationally expensive because each guess requires thousands of iterations.

Choose a work factor that balances security with authentication performance. A work factor of 10-12 is typical for most applications. Re-evaluate as hardware improves.

7. What is the purpose of the SecurityContextHolder and how does it differ from the HTTP session?

SecurityContextHolder is the runtime container for Spring Security's authentication information. It holds the SecurityContext containing the authenticated principal, credentials, and granted authorities. By default, it uses a ThreadLocal to make the security context available to any code running in the current thread.

The HTTP session stores the SecurityContext persistently across requests for session-based authentication. When a request arrives, SecurityContextPersistenceFilter reads the context from the session and populates the SecurityContextHolder. When the response finishes, the context is saved back to the session if it was modified.

For HTTP Basic, no HTTP session exists, so each request must provide credentials. Spring Security extracts them from the Authorization header, creates an authenticated token, and places it in the SecurityContextHolder for that single request only.

8. How does Spring Security's filter chain decide which authentication mechanism to use when both form login and HTTP Basic are configured?

Spring Security's filter chain does not "choose" between mechanisms in the way a conditional logic would. Instead, both UsernamePasswordAuthenticationFilter and BasicAuthenticationFilter are present in the chain, and each processes requests independently.

For form login, UsernamePasswordAuthenticationFilter only processes POST requests to the login processing URL (default /login). It extracts username and password from the request body and attempts authentication. If the request is not a POST to /login, this filter does nothing.

For HTTP Basic, BasicAuthenticationFilter checks for the Authorization header on every request. If present, it extracts and validates credentials. If absent, the filter chain continues without authenticating.

If both mechanisms could apply (e.g., a browser POSTing to /login), form login takes precedence because it is earlier in the filter chain by default. You can reorder filters or use authenticationDetailsSource to customize this behavior.

9. What are the security implications of storing the SecurityContext in the HTTP session versus using a stateless token approach?

Storing the SecurityContext in the HTTP session creates server-side state that can be invalidated on logout or timeout. Sessions can be invalidated server-side, making revocation immediate. However, sessions require session storage infrastructure, limit horizontal scalability without sticky sessions or shared session stores, and are vulnerable to session hijacking if cookies are stolen.

Stateless token approaches (like JWT) move authentication state to the client. The server validates the token signature on each request without consulting a session store, making horizontal scaling straightforward. However, token revocation is difficult because the server must maintain a blacklist or use short-lived tokens. Tokens can be stolen and used until they expire, and sensitive data in the token payload is visible to anyone who can decode the Base64 payload (though not modify it without the secret).

Choose sessions for applications where immediate revocation matters and where horizontal scaling can be managed. Choose stateless tokens for APIs consumed by many clients or when scaling without sticky sessions is a priority.

10. How does SessionCreationPolicy.STATELESS affect Spring Security's behavior, and when should you use it?

SessionCreationPolicy.STATELESS instructs Spring Security never to create an HTTP session during the request processing. It sets the HttpSession to null and prevents SecurityContextPersistenceFilter from saving or loading the security context from session storage.

Use this policy when building purely stateless APIs that authenticate via mechanisms like HTTP Basic, API keys, or JWT tokens. It reduces memory usage, eliminates session-related attack vectors like session hijacking, and simplifies horizontal scaling.

Do not use STATELESS with form login because form login relies on sessions to persist authentication across requests. Attempting to combine them results in every request requiring fresh authentication since no session exists to store the security context.

11. What is the WWW-Authenticate header in HTTP Basic authentication, and how does it affect client behavior?

The WWW-Authenticate header is sent by a server in a 401 response to indicate that authentication is required. For HTTP Basic, it includes the realm name: WWW-Authenticate: Basic realm="RealmName". The realm identifies the protection space and is displayed to the user in the browser's authentication dialog.

When a client like a browser sees this header, it prompts the user for credentials and resends the request with the Authorization header populated. API clients like curl can preemptively send credentials in the Authorization header to avoid the 401 round-trip.

If your API is consumed by both browsers and programmatic clients, returning the WWW-Authenticate header ensures browsers trigger the native authentication dialog while API clients can still authenticate successfully.

12. How does Remember-Me authentication work in Spring Security, and what are its security trade-offs?

Remember-me authentication allows the application to remember a user's identity across browser sessions without requiring them to log in again. When enabled, Spring Security issues a cookie containing a token after successful form login. On subsequent sessions, if the session expires but the remember-me cookie is present and valid, the user is automatically authenticated.

The default implementation uses TokenBasedRememberMeServices, which generates a signed token containing the username, expiration time, and MD5 hash. This token is vulnerable to network sniffing if HTTPS is not enforced. The more secure PersistentTokenBasedRememberMeServices stores tokens in a database, enabling revocation.

Security trade-offs include: the remember-me token is a long-lived credential that can be stolen and abused if not protected, it requires careful HTTPS enforcement, and it bypasses password-based re-authentication for sensitive operations.

13. What is the difference between permitAll() and authenticated() in Spring Security authorization configuration?

permitAll() configures Spring Security to allow requests matching the specified pattern without requiring any authentication. These paths are accessible to anonymous users and bypass the security filter chain's authentication checks.

authenticated() requires that any request matching the pattern must be successfully authenticated before the request proceeds. Unauthenticated requests are redirected to the login page (for form login) or receive a 401 with WWW-Authenticate header (for HTTP Basic).

Common patterns include permitAll() for static resources, login endpoints, and public pages, with authenticated() for all other endpoints. Mixing these correctly ensures public paths remain accessible while protecting sensitive resources.

14. How does DelegatingPasswordEncoder handle password migration from legacy hashing algorithms?

DelegatingPasswordEncoder supports multiple password encoding algorithms simultaneously by storing a prefix in the encoded password that identifies which encoder to use for verification. This enables gradual migration from weak legacy encoders (like MD5 or SHA-1) to strong encoders like BCrypt.

Each stored password has the format: {encoderId}encodedPassword. When verifying a submitted password, DelegatingPasswordEncoder extracts the prefix, selects the appropriate encoder, and verifies the password. If the legacy encoder matches, you can re-encode the password with BCrypt and update the storage.

This migration strategy allows existing passwords to continue working while new passwords and re-authenticating users get the stronger encoding. The migration is complete once all legacy passwords have been replaced.

15. What is the role of AuthenticationFailureHandler and AuthenticationSuccessHandler in form login, and when would you customize them?

AuthenticationFailureHandler processes what happens after a failed authentication attempt. Spring Security provides implementations like SimpleUrlAuthenticationFailureHandler (redirect to a URL), ExceptionMappingAuthenticationFailureHandler (redirect based on exception type), and DelegatingAuthenticationFailureHandler (delegate to different handlers per exception).

AuthenticationSuccessHandler processes what happens after successful authentication. Implementations include SavedRequestAwareAuthenticationSuccessHandler (redirect to originally requested URL), SimpleUrlAuthenticationSuccessHandler (redirect to fixed URL), and ForwardAuthenticationSuccessHandler (forward to a URL instead of redirect).

Customize these handlers when you need to log authentication events to a database, redirect to different pages based on user roles, return JSON responses for API clients instead of redirects, or integrate with third-party audit logging systems.

16. How does Spring Security's CSRF protection interact with Single Page Applications that use form login?

Single Page Applications (SPAs) that use form login face a CSRF challenge because the login form must include a valid CSRF token, but the SPA's JavaScript must obtain this token before posting. Spring Security exposes the CSRF token via the CsrfToken request attribute, which can be fetched via an AJAX request and included as a header or form field.

For SPAs, the typical flow is: a protected page load triggers a request to fetch the CSRF token, the token is stored in memory or a variable, subsequent POST/PUT/DELETE requests include the token in a header like X-CSRF-TOKEN, and the server validates the token on each mutating request.

Some SPAs bypass CSRF by using token-based authentication (like JWT in the Authorization header) instead of cookie-based form login, eliminating the CSRF attack surface entirely at the cost of losing session-based authentication benefits.

17. What is the difference between invalidateHttpSession(true) and invalidateHttpSession(false) during logout?

invalidateHttpSession(true) (the default) immediately invalidates the HTTP session, destroying all session data including the SecurityContext. The session ID changes with the response, making old session IDs invalid.

invalidateHttpSession(false) clears the security context but leaves the HTTP session active. The session can still be used for anonymous browsing after logout, which some applications want to preserve for analytics or shopping cart functionality.

Always call SecurityContextHolder.clearContext() explicitly to ensure the security context is cleared regardless of session invalidation behavior. Consider whether your application needs session data after logout before choosing the invalidation strategy.

18. How does the maximumSessions(1) setting prevent concurrent session attacks, and what are its limitations?

maximumSessions(1) in Spring Security's session management limits a user to a single active session. When a user logs in from a second device or browser, the older session is invalidated upon new authentication. This prevents account sharing and reduces the window for session hijacking.

With maxSessionsPreventsLogin(false) (default), the old session is invalidated and the new one succeeds. With maxSessionsPreventsLogin(true), the new login is prevented while the old session remains active.

Limitations include: it only works with session-based authentication (HTTP Basic is stateless), it requires session registry configuration in clustered environments, and it does not prevent an attacker from hijacking a session before the legitimate user logs in from another device.

19. What happens when a user tries to access a protected resource without being authenticated, and how does the AuthenticationEntryPoint control this behavior?

When an unauthenticated user requests a protected resource, Spring Security's ExceptionTranslationFilter catches the AccessDeniedException and delegates to the configured AuthenticationEntryPoint to handle the authentication challenge.

For form login, the default LoginUrlAuthenticationEntryPoint redirects (HTTP 302) the user to the login page with a continue parameter preserving the original request URL.

For HTTP Basic, the default BasicAuthenticationEntryPoint returns HTTP 401 with a WWW-Authenticate header containing the realm. For APIs, you might customize this to return JSON: {"error": "Authentication required"} with HTTP 401.

Custom entry points are useful when a single application serves both browser and API clients, requiring different authentication challenge formats based on the Accept header.

20. How does Spring Security integrate with the Java Authentication SPI (JAAS) and when would you use it?

Spring Security's JaasAuthenticationProvider bridges Spring Security authentication with JAAS (Java Authentication and Authorization Service). JAAS is a pluggable authentication API used in legacy Java EE applications and Java OS-level login modules.

Use this integration when modernizing legacy applications that already have JAAS-based authentication, when integrating with enterprise identity stores that provide JAAS login modules (like Kerberos or smart card authentication), or when an organization has standardized on JAAS for all Java authentication.

The integration involves configuring JaasAuthenticationProvider with a JAAS configuration file (typically jaas.config) and a service name that maps to the correct login module. Spring Security converts its Authentication object into a JAAS Subject for the login module to process.

Further Reading

Deepen your understanding of Spring Security authentication with these resources.

Conclusion

Category

Related Posts

Spring Boot Build Tools: Maven & Gradle

Configure Maven and Gradle for Spring Boot projects—plugins, dependency management, packaging JARs and WARs, and build automation essentials.

#spring-boot #spring-boot-roadmap #learning-path

Embedded Web Servers in Spring Boot: Tomcat, Jetty, Undertow

Configure embedded servers in Spring Boot: compare Tomcat, Jetty, and Undertow, tune thread pools, enable access logs, and switch implementations.

#spring-boot #spring-boot-roadmap #learning-path

JUnit 5 & Jupiter: Lifecycle, Nested & Parameterized Tests

Explore JUnit 5 Jupiter features: master test lifecycle annotations, organize tests with @Nested, and parameterize tests with @CsvSource and @MethodSource.

#spring-boot #spring-boot-roadmap #learning-path