Spring Security Interview Questions – Architecture and Authentication

  • Last Updated: July 2, 2026
  • By: javahandson
  • Series
img

Spring Security Interview Questions – Architecture and Authentication

Master spring security interview questions: filter chain, SecurityFilterChain, AuthenticationManager, UserDetailsService, BCrypt, and more.

Security is one of the most heavily tested areas in any Spring interview. Nearly every production application built with Spring eventually needs to answer one question: who is calling this endpoint, and what are they allowed to do?

These Spring Security interview questions test exactly that boundary — not just whether you can wire up a login form, but whether you understand what happens inside the framework when a request arrives, how the filter chain intercepts it, and how authentication state is created, stored, and propagated through the request. Every example in this article is built directly on the Spring Security Framework’s own configuration API, so the mechanics you see here are the same ones running underneath any higher-level setup you might use later.

This article covers 15 questions on:

  • The Spring Security Filter Chain and SecurityFilterChain in Spring Security 6.x
  • UsernamePasswordAuthenticationFilter and SecurityContextHolder
  • AuthenticationManager, AuthenticationProvider, and UserDetailsService
  • Password encoding with BCrypt
  • The WebSecurityConfigurerAdapter deprecation
  • DaoAuthenticationProvider, ExceptionTranslationFilter, and @EnableWebSecurity
  • Form login vs HTTP Basic authentication
📌 How to use this article:   Read each question as if the interviewer just asked it. Then read the answer. The Interview Insight notes highlight the specific detail that separates average candidates from strong ones.

Beginner Level — Q1 to Q5

Q1What is Spring Security, and what does it provide out of the box?

Answer – Spring Security is the standard framework for securing Spring applications. It handles the two core concerns every secured app needs — authentication (who are you?) and authorization (what can you do?) — and integrates cleanly with the rest of the Spring ecosystem, using AOP-style method security and the standard Servlet filter pipeline.

Spring Security is deliberately explicit rather than automatic. It does not assume you want every endpoint locked down or a particular login mechanism — instead, it provides a complete toolkit and expects you to specify what you want via its configuration API. This is a design choice, not a limitation: it means the same mental model scales from a five-endpoint internal tool to a large multi-team application with dozens of authentication and authorization rules.

Setting up Spring Security in a plain Spring application takes exactly two pieces: the dependencies on the classpath, and a SecurityFilterChain bean that declares your rules. Nothing is inferred for you — every rule you want enforced has to be written explicitly, which is exactly what makes the framework predictable to reason about in an interview and in production.

<!-- Required dependencies -->
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-config</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-web</artifactId>
</dependency>
@Configuration
@EnableWebSecurity
public class SecurityConfig {
 
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/public/**").permitAll()
                .anyRequest().authenticated())
            .formLogin(Customizer.withDefaults())
            .httpBasic(Customizer.withDefaults());
 
        return http.build();
    }
 
    @Bean
    public UserDetailsService userDetailsService(PasswordEncoder encoder) {
        UserDetails user = User.withUsername("admin")
            .password(encoder.encode("secret123"))
            .roles("ADMIN")
            .build();
        return new InMemoryUserDetailsManager(user);
    }
 
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

With this configuration in place, every request except /public/** now requires authentication, a login form is available, and credentials are checked against the in-memory user store you defined. Nothing here happened by accident — each line maps directly to a capability you asked for.

What Spring Security gives you once configured:

  • A pluggable authentication model — AuthenticationManager, AuthenticationProvider, and UserDetailsService work together to verify who is calling.
  • A Servlet Filter chain — intercepts every matching request before it reaches your controller, enforcing your rules consistently.
  • Built-in protections — CSRF, session fixation, and clickjacking defenses are available with a single configuration call each.
  • Method-level security — @PreAuthorize, @PostAuthorize, and @Secured let you enforce rules at the service layer, not just at the URL level.
  • Extension points for modern auth — OAuth2, JWT, LDAP, and SAML all plug into the same AuthenticationProvider architecture.

Bottom line: Spring Security’s philosophy is explicit configuration over inferred behavior — you declare exactly what you want protected, how, and against what, and the framework enforces it consistently on every request.

🎯 Interview Insight: A strong answer here goes beyond ‘it secures Spring apps.’ Explaining that Spring Security is explicit by design — you declare a SecurityFilterChain, and it enforces exactly those rules — shows you understand the architecture rather than having only seen a preconfigured project.

Q2.  What is Authentication vs Authorization?

Answer – Authentication and authorization are the two pillars of Spring Security. They are implemented by different components, and interviewers frequently probe whether you can draw the exact line between them.

The distinction matters beyond definitions — it maps directly to which component in the framework is responsible and to which HTTP status code a failure should return. Getting the mapping right is what a senior answer looks like.

Quick Comparison Table

AspectAuthenticationAuthorization
Question answeredWho are you?What can you do?
RunsFirstSecond (after identity is known)
Key componentAuthenticationManager / AuthenticationProviderAuthorizationFilter / @PreAuthorize
Failure status401 Unauthorized403 Forbidden
Example mechanismUsername/password, JWT signature checkhasRole(“ADMIN”), @PreAuthorize

Authentication — “Who are you?”

Authentication verifies a caller’s identity — checking a username and password, validating a JWT’s signature, or trusting an external identity provider’s assertion. In Spring Security, this is the job of AuthenticationManager, which delegates to one or more AuthenticationProvider implementations. The output is an Authentication object, stored in the SecurityContext for the rest of the request.

Authorization — “What are you allowed to do?”

Authorization occurs after identity is known and determines whether that identity is permitted to access a specific resource or action. It is enforced either at the HTTP layer via authorizeHttpRequests() rules or at the method layer via @PreAuthorize, @PostAuthorize, and @Secured.

http
    .authorizeHttpRequests(authorize -> authorize
        .requestMatchers("/admin/**").hasRole("ADMIN")   // AUTHORIZATION rule
        .requestMatchers("/public/**").permitAll()
        .anyRequest().authenticated()                     // requires AUTHENTICATION
    )
    .httpBasic(Customizer.withDefaults());                // AUTHENTICATION mechanism
 
@PreAuthorize("hasRole('ADMIN')")   // method-level AUTHORIZATION
public void deleteUser(Long id) { ... }

Bottom line: A request can fail authentication (we don’t know who you are → 401) or fail authorization (we know who you are, but you’re not allowed here → 403). Getting this pairing right is the whole point of the question.

🎯 Interview Insight: Interviewers love asking: ‘A request returns 403 — what does that tell you?’ The correct answer is that authentication already succeeded, because Spring Security only reaches the authorization check after it knows who the caller is.

Q3.  What is the Spring Security Filter Chain — how is it structured and ordered?

Answer – The Spring Security Filter Chain is a series of Servlet Filter objects that every HTTP request passes through before it reaches your controller. Spring Security hooks into the Servlet container itself, not directly into Spring MVC — a deliberate design choice that lets it protect every request, including those that never reach a Spring MVC controller.

Each filter in the chain has one narrow job, and the order they run in is significant: an authentication filter must run before an authorization filter, or there would be no identity yet to authorize.

Quick Comparison Table

ComponentResponsibility
DelegatingFilterProxyThe single Servlet entry point, registered as springSecurityFilterChain, bridges the Servlet container to Spring’s bean context
FilterChainProxyHolds the actual ordered list of security filters and runs each request through them in sequence
Individual filtersEach has one job — e.g., CsrfFilter checks tokens, UsernamePasswordAuthenticationFilter handles login POSTs

Typical filter order for a default form-login app:

  • DisableEncodeUrlFilter
  • WebAsyncManagerIntegrationFilter
  • SecurityContextHolderFilter
  • HeaderWriterFilter
  • CsrfFilter
  • LogoutFilter
  • UsernamePasswordAuthenticationFilter
  • DefaultLoginPageGeneratingFilter
  • BasicAuthenticationFilter
  • ExceptionTranslationFilter
  • AuthorizationFilter
// Print the actual filter chain for your app at startup — great for interviews and debugging
@Bean
public ApplicationListener<ContextRefreshedEvent> logFilters(FilterChainProxy filterChainProxy) {
    return event -> filterChainProxy.getFilterChains().forEach(chain ->
        chain.getFilters().forEach(f -> System.out.println(f.getClass().getSimpleName()))
    );
}

A request that fails at any filter does not proceed further — if CsrfFilter rejects a missing token, the request never reaches the authentication filter at all. This is also how the chain is extended: you can insert a custom filter at a precise position using addFilterBefore(), addFilterAfter(), or addFilterAt(), which is exactly how most JWT implementations are wired in.

Bottom line: DelegatingFilterProxy is the bridge into the Servlet container; FilterChainProxy is where the real, ordered security logic lives.

🎯 Interview Insight: Being able to say ‘DelegatingFilterProxy delegates to FilterChainProxy, which holds the ordered list of security filters’ is the single most useful sentence in a Spring Security architecture interview.

Q4.  What is SecurityFilterChain — how is it built in Spring Security 6.x?

Answer: SecurityFilterChain represents a specific ordered filter chain, along with a RequestMatcher that determines which requests it applies to. In Spring Security 6.x, it is the only supported way to configure security — WebSecurityConfigurerAdapter has been removed entirely.

@EnableWebSecurity on the @Configuration class is what activates the infrastructure that picks up your SecurityFilterChain bean and wires it into the Servlet container. Without it, the bean is just an inert object sitting in the ApplicationContext — it’s never consulted for any incoming request.

@Configuration
@EnableWebSecurity
public class SecurityConfig {
 
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(authorize -> authorize
                .requestMatchers("/api/public/**").permitAll()
                .requestMatchers("/api/admin/**").hasRole("ADMIN")
                .anyRequest().authenticated()
            )
            .csrf(csrf -> csrf.disable())
            .sessionManagement(session -> session
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS))
            .httpBasic(Customizer.withDefaults());
 
        return http.build();
    }
 
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

Key points:

  • Fluent DSL — authorizeHttpRequests, csrf, sessionManagement, formLogin, httpBasic, and oauth2Login all return the same HttpSecurity builder.
  • http.build() — assembles the ordered Filter list and returns the finished bean.
  • Multiple chains — one app can define several SecurityFilterChain beans, each scoped with securityMatcher().
  • @Order matters — when multiple chains exist, the first one whose matcher fits the request wins.

Multiple SecurityFilterChain beans, scoped by URL

@Bean
@Order(1)
public SecurityFilterChain apiFilterChain(HttpSecurity http) throws Exception {
    http.securityMatcher("/api/**")
        .authorizeHttpRequests(a -> a.anyRequest().authenticated())
        .sessionManagement(s -> s.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
    return http.build();
}
 
@Bean
@Order(2)
public SecurityFilterChain webFilterChain(HttpSecurity http) throws Exception {
    http.authorizeHttpRequests(a -> a.anyRequest().authenticated())
        .formLogin(Customizer.withDefaults());
    return http.build();
}

Bottom line: This is the modern replacement for stacking multiple WebSecurityConfigurerAdapter subclasses — each chain is just a bean, explicitly scoped and ordered.

🎯 Interview Insight: A very common 6.x trap: forgetting to call http.build() and return it. Every rule mutates the same builder, but the SecurityFilterChain only exists once build() is called and returned as a @Bean.

Q5.  What is UsernamePasswordAuthenticationFilter — what happens internally?

Answer – UsernamePasswordAuthenticationFilter is the Servlet Filter responsible for handling traditional form-based login. It extends AbstractAuthenticationProcessingFilter and, by default, listens for POST requests to /login, reading the username and password request parameters.

This filter is registered automatically the moment you call formLogin() inside your SecurityFilterChain configuration — it’s core Spring Security behavior, wired in by that one method call.

What happens internally:

a.  attemptAuthentication() reads the two request parameters.

b.  It packages them into an unauthenticated UsernamePasswordAuthenticationToken (isAuthenticated() = false, raw password still present).

c.  The token is handed to authenticationManager.authenticate(token).

d.  AuthenticationManager delegates to the right AuthenticationProvider (typically DaoAuthenticationProvider), which loads the user and checks the password.

// Simplified view of what happens inside attemptAuthentication()
public Authentication attemptAuthentication(HttpServletRequest request,
                                             HttpServletResponse response) {
    String username = obtainUsername(request);
    String password = obtainPassword(request);
 
    UsernamePasswordAuthenticationToken authRequest =
        UsernamePasswordAuthenticationToken.unauthenticated(username, password);
 
    return this.getAuthenticationManager().authenticate(authRequest);
}

On success vs on failure:

  • Success — successfulAuthentication() stores the fully authenticated token in SecurityContextHolder and redirects to the originally requested URL (or “/”).
  • Failure — unsuccessfulAuthentication() clears the context and redirects to /login?error by default.

Bottom line: The filter only extracts credentials and builds the token — it never checks passwords itself. That verification logic always lives in an AuthenticationProvider.

🎯 Interview Insight: A precise answer distinguishes the filter’s job from the provider’s job. UsernamePasswordAuthenticationFilter only extracts credentials from the HTTP request and builds the token — it does not know how passwords are checked.

Intermediate Level — Q6 to Q11

Q6.  What is SecurityContextHolder — how does it store authentication across a request?

Answer – SecurityContextHolder makes the currently authenticated user available anywhere in the application without passing it as a method parameter. It holds a SecurityContext, which in turn holds the Authentication object for the current caller.

By default, it uses thread-local storage, which ensures that two concurrent requests from different users — handled on two different Servlet threads — can safely see only their own identity.

Quick Comparison Table — storage strategies

StrategyBehaviorUse case
MODE_THREADLOCAL (default)Each thread gets its own isolated SecurityContextStandard synchronous web requests
MODE_INHERITABLETHREADLOCALPropagates the context to child threads spawned from the request thread@Async methods, manually spawned worker threads
MODE_GLOBALOne context shared across the entire JVMRare — typically only simple standalone/batch apps
// Reading the current authenticated user from anywhere in the code
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
String username = authentication.getName();
Collection<? extends GrantedAuthority> roles = authentication.getAuthorities();
 
// Switching thread-local propagation to child threads (e.g. for @Async methods)
SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);

Per-request lifecycle:

  • SecurityContextHolderFilter loads the context at the start of the request (from the session, or reconstructed from a token for stateless APIs)
  • It clears the context once the request completes, so a pooled thread never leaks identity into the next request
  • In Spring Security 6, SecurityContextHolderFilter replaced the older SecurityContextPersistenceFilter and no longer eagerly saves the context back to the session

Bottom line: ThreadLocal is what makes SecurityContextHolder safe for concurrent requests — and exactly what breaks it across thread-pool boundaries like @Async.

🎯 Interview Insight: A frequent follow-up: ‘Why can @Async methods lose the authenticated user?’ Because ThreadLocal storage does not propagate to a new thread from a pool. Fix it with MODE_INHERITABLETHREADLOCAL or Spring’s DelegatingSecurityContextExecutor.

Q7.  What is the Authentication object — what does it hold?

Answer – Authentication is the core interface that represents both an in-progress authentication attempt and, upon success, the identity of the logged-in caller. It extends java.security.Principal.

It carries three pieces of information that come up constantly in interviews, and knowing exactly what each one holds — and when it is cleared — is what distinguishes a memorized definition from real understanding.

Quick Comparison Table

FieldHoldsNotes
PrincipalThe caller’s identity — usually a UserDetails objectJust the username as a String before authentication succeeds
CredentialsWhatever proves identity — usually the passwordErased after successful authentication
AuthoritiesCollection of GrantedAuthority (roles/permissions)Drives every authorization decision afterward
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
 
Object principal = authentication.getPrincipal();      // usually a UserDetails instance
Object credentials = authentication.getCredentials();  // usually null after success (erased)
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
boolean isAuthenticated = authentication.isAuthenticated();
String name = authentication.getName();                // shortcut for principal's username
 
if (authentication.getPrincipal() instanceof MyUserDetails userDetails) {
    Long userId = userDetails.getId();
}

Before authentication succeeds, an unauthenticated token contains only the submitted username and raw password, and isAuthenticated() returns false. Once an AuthenticationProvider verifies the credentials, a new, fully populated, authenticated Authentication object is produced — treated as effectively immutable from that point on.

Bottom line: Principal answers who; credentials prove it (briefly); authorities decide what happens next.

🎯 Interview Insight: Interviewers sometimes ask why getCredentials() returns null after login. It’s deliberate — DaoAuthenticationProvider erases credentials specifically so the raw password doesn’t sit in the SecurityContext, the session, or a heap dump.

Q8.  What are AuthenticationManager and AuthenticationProvider?

Answer – These two interfaces collaborate to determine whether a set of credentials is valid — understanding the delegation between them is one of the most-tested aspects of Spring Security architecture.

The split exists so that Spring Security can support multiple independent authentication mechanisms side by side, without any of them knowing the others exist.

Quick Comparison Table

AspectAuthenticationManagerAuthenticationProvider
RoleEntry point and delegatorDoes the actual credential verification
Default implementationProviderManagerDaoAuthenticationProvider (and others)
Verifies passwords itself?NoYes
How many per app?Usually oneCan register several — one per auth mechanism

AuthenticationManager — the delegator

Defines a single method, authenticate(Authentication), and is the entry point every authentication filter calls into. Its default implementation, ProviderManager, does not verify anything itself — it holds an ordered list of AuthenticationProvider beans and asks each one if it supports(authentication.getClass()) until it finds one willing to handle that Authentication type.

AuthenticationProvider — where verification actually happens

Each provider specializes in one kind of authentication: DaoAuthenticationProvider verifies username/password against a UserDetailsService and PasswordEncoder; JwtAuthenticationProvider verifies a JWT’s signature and claims; LdapAuthenticationProvider authenticates against an LDAP directory. An application can register multiple providers, and ProviderManager tries each in turn.

@Bean
public AuthenticationManager authenticationManager(HttpSecurity http,
                                                     PasswordEncoder encoder,
                                                     UserDetailsService uds) throws Exception {
    AuthenticationManagerBuilder builder =
        http.getSharedObject(AuthenticationManagerBuilder.class);
 
    builder.authenticationProvider(daoAuthenticationProvider(encoder, uds));
    // builder.authenticationProvider(ldapAuthenticationProvider());   // multiple providers allowed
 
    return builder.build();
}
 
// ProviderManager's core loop (simplified):
// for (AuthenticationProvider provider : providers) {
//     if (!provider.supports(authentication.getClass())) continue;
//     result = provider.authenticate(authentication);
//     if (result != null) return result;   // first success wins
// }
// throw new ProviderNotFoundException(...);

Bottom line: This chain-of-responsibility design is exactly what allows one app to accept both form login credentials and JWT bearer tokens side by side.

🎯 Interview Insight: A concise, correct answer: ‘AuthenticationManager is the entry point and delegator; AuthenticationProvider is where the actual credential verification happens.’ Naming ProviderManager and its iteration loop elevates the answer to the architecture level.

Q9.  What is UserDetailsService and UserDetails — how do you implement custom authentication?

Answer – These two interfaces connect Spring Security’s generic authentication machinery to your application’s actual user data — typically a database table, though it could be any source.

Both interfaces are part of core Spring Security. Once your implementation is registered as a Spring bean — via @Service or a @Bean method — the rest of the framework picks it up automatically wherever authentication needs to look up a user.

Quick Comparison Table

InterfacePurposeYou implement
UserDetailsServiceLooks up a user by usernameloadUserByUsername(String)
UserDetailsDescribes the user the way Spring Security needs to see themusername, password, authorities, 4 status flags
@Entity
public class AppUser implements UserDetails {
    @Id @GeneratedValue private Long id;
    private String username;
    private String password;     // stored ENCODED, never plain text
    private boolean enabled = true;
 
    @ManyToMany(fetch = FetchType.EAGER)
    private Set<Role> roles;
 
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return roles.stream()
            .map(r -> new SimpleGrantedAuthority("ROLE_" + r.getName()))
            .collect(Collectors.toSet());
    }
 
    @Override public String getPassword() { return password; }
    @Override public String getUsername() { return username; }
    @Override public boolean isAccountNonExpired() { return true; }
    @Override public boolean isAccountNonLocked() { return true; }
    @Override public boolean isCredentialsNonExpired() { return true; }
    @Override public boolean isEnabled() { return enabled; }
}
 
@Service
public class AppUserDetailsService implements UserDetailsService {
 
    private final AppUserRepository repository;
 
    public AppUserDetailsService(AppUserRepository repository) {
        this.repository = repository;
    }
 
    @Override
    public UserDetails loadUserByUsername(String username) {
        return repository.findByUsername(username)
            .orElseThrow(() -> new UsernameNotFoundException("No user: " + username));
    }
}

Bonus — keep UserDetails separate from your JPA entity

Many production codebases deliberately avoid implementing UserDetails directly on the JPA entity, and instead write a thin UserDetailsAdapter that wraps it. This keeps Spring Security concerns out of the persistence model and avoids lazy-loading exceptions when roles are accessed outside a transaction.

Bottom line: Once your UserDetailsService is a bean, DaoAuthenticationProvider picks it up automatically — form login, HTTP Basic, remember-me and method security all work against real database users with no further wiring.

🎯 Interview Insight: A subtle detail interviewers probe for: UserDetails and your JPA entity don’t have to be the same class. Keeping them separate is often the cleaner production choice.

Q10.  What is PasswordEncoder — why BCrypt, what is the work factor?

Answer – PasswordEncoder hashes passwords for storage and verifies a submitted password against a stored hash, without ever storing or comparing plain text. It exposes encode(rawPassword) and matches(rawPassword, encodedPassword).

Spring Security ships several PasswordEncoder implementations, and interviewers often want you to name more than just BCrypt to show you understand why the others are discouraged.

Quick Comparison Table

EncoderSalted?Deliberately slow?Recommended?
NoOpPasswordEncoderNoNoNever — plain text, testing only
MD5 / SHA-256No (unless manual)NoNo — fast hashing is unsuitable for passwords
BCryptPasswordEncoderYes, automaticYes, tunable work factorYes — the default recommendation
Argon2PasswordEncoderYes, automaticYes, tunable memory + time costYes — stronger, more resource-intensive
Pbkdf2PasswordEncoderYes, automaticYes, tunable iterationsAcceptable — common in FIPS-compliance contexts

Why BCrypt specifically

BCrypt is deliberately slow, built for password storage rather than general hashing. It automatically generates and embeds a random salt into every hash, so two identical passwords produce completely different stored hashes, defeating rainbow-table attacks. Its work factor (default 10) controls how many rounds of key expansion it performs — each increment roughly doubles the cost, keeping brute-force attacks slow even as hardware improves.

@Bean
public PasswordEncoder passwordEncoder() {
    // Work factor 12 instead of default 10 — roughly 4x slower per hash
    return new BCryptPasswordEncoder(12);
}
 
String hashed = passwordEncoder.encode("myPlainTextPassword");
// $2a$12$eImiTXuWVxfM37uY4JANjQ==...   <- algorithm id, work factor, salt, hash
 
boolean matches = passwordEncoder.matches("myPlainTextPassword", hashed);

DelegatingPasswordEncoder — the actual Spring Security default:

  • Prefixes every stored hash with an identifier, e.g., {bcrypt}$2a$10$…
  • Picks the correct underlying encoder at verification time based on that prefix
  • Let’s you migrate hashing algorithms later without invalidating existing stored passwords

Bonus — stronger alternative: Argon2

@Bean
public PasswordEncoder passwordEncoder() {
    // Winner of the Password Hashing Competition — tunable memory AND time cost
    return new Argon2PasswordEncoder(16, 32, 1, 1 << 16, 2);
}

Bottom line: Use BCrypt as the safe default; reach for Argon2 when you specifically need resistance to GPU/ASIC cracking at scale.

🎯 Interview Insight: A question that catches out default-only users: ‘Why not SHA-256 for passwords?’ SHA-256 is designed to be fast — exactly the wrong property for password hashing, since it makes brute-force cracking cheap. BCrypt’s deliberate, tunable slowness is the actual security feature.

Q11.  What is WebSecurityConfigurerAdapter — why was it deprecated in Spring Security 5.7?

Answer – WebSecurityConfigurerAdapter was, for years, the standard base class for configuring Spring Security — extend it, override configure(HttpSecurity). It was deprecated in 5.7 and fully removed in Spring Security 6, replaced by the component-based SecurityFilterChain approach.

This change is a Spring Security framework decision, part of the broader shift the Spring team has made toward composition over inheritance across the ecosystem.

Quick Comparison Table

AspectWebSecurityConfigurerAdapter (old)SecurityFilterChain @Bean (current)
Configuration styleClass inheritance — extend and overrideComposition — return a @Bean
Multiple configsAwkward nested static classesMultiple beans with @Order + securityMatcher()
TestabilityHarder — tied to subclassingEasier — a bean like any other
StatusRemoved in Spring Security 6Mandatory from 5.7 onward

Old approach — deprecated in 5.7, removed in 6.x

@Configuration
@EnableWebSecurity
public class OldSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/admin/**").hasRole("ADMIN")
                .anyRequest().authenticated()
            .and()
            .httpBasic();
    }
}

Current approach — the only supported style

@Configuration
@EnableWebSecurity
public class NewSecurityConfig {
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.authorizeHttpRequests(auth -> auth
                .requestMatchers("/admin/**").hasRole("ADMIN")
                .anyRequest().authenticated())
            .httpBasic(Customizer.withDefaults());
        return http.build();
    }
}

Why was it deprecated:

  • Composition over inheritance — forcing subclassing for configuration conflicted with Spring’s broader design direction.
  • Only one config per class — supporting multiple chains needed confusing nested static classes.
  • API evolution — protected methods exposed on the class made it hard to change HttpSecurity without breaking every subclass.
  • Testability — a SecurityFilterChain bean is just a bean — easier to unit test than an overridden method.

Bottom line: If you see extends WebSecurityConfigurerAdapter in a tutorial, treat it as Spring Security 5.6 or earlier — it will not compile against Spring Security 6.x.

🎯 Interview Insight: One of the most common ‘have you kept up’ questions in 2026 interviews, since older tutorials still show extends WebSecurityConfigurerAdapter. Explaining not just that it changed but why signals genuinely current knowledge.

Advanced Level — Q12 to Q15

Q12.  What is DaoAuthenticationProvider?

Answer – DaoAuthenticationProvider is the default, most widely used AuthenticationProvider for username/password authentication. It authenticates a user by retrieving stored credentials through a UserDetailsService and comparing them with a PasswordEncoder.

Its name reflects its role: it is a Data Access Object-style provider — it fetches user data through a UserDetailsService, the same way a DAO fetches data through a repository, then hands off the actual credential comparison to a PasswordEncoder.

Internal flow (AbstractUserDetailsAuthenticationProvider.authenticate):

a.  retrieveUser() calls UserDetailsService.loadUserByUsername().

b.  Pre-authentication checks (DefaultPreAuthenticationChecks) verify the account is enabled, not locked, not expired — fails fast with DisabledException/LockedException if not.

c.  additionalAuthenticationChecks() calls PasswordEncoder.matches() — throws BadCredentialsException on mismatch.

d.  Post-authentication checks verify credentials haven’t expired.

e.  Returns a new, fully authenticated token with credentials erased.

@Bean
public DaoAuthenticationProvider daoAuthenticationProvider(UserDetailsService uds,
                                                             PasswordEncoder encoder) {
    DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
    provider.setUserDetailsService(uds);
    provider.setPasswordEncoder(encoder);
    // Optional: hide "user not found" vs "bad password" distinction in the exception thrown
    provider.setHideUserNotFoundExceptions(true);
    return provider;
}

Quick Comparison Table — exception thrown by check phase

PhaseFails withEven with correct password?
Pre-authentication checksDisabledException / LockedException / AccountExpiredExceptionYes — checked before the password
Password checkBadCredentialsExceptionN/A — this is the password check itself
Post-authentication checksCredentialsExpiredExceptionYes — checked after the password matches

Bottom line: Because account-status checks run before password verification, a locked account fails with LockedException, not BadCredentialsException — even with the correct password.

🎯 Interview Insight: The detail that separates a memorized answer from a genuinely understood one: pre-authentication checks run before the password is even checked. Knowing that ordering is exactly what senior interviews probe for.

Q13.  What is ExceptionTranslationFilter — what does it do?

Answer – ExceptionTranslationFilter sits in the chain before AuthorizationFilter, and its job is to catch two security-specific exceptions — AuthenticationException and AccessDeniedException — and translate them into a meaningful HTTP response.

Without this filter, a raw exception propagating out of the filter chain would not, by itself, produce a sensible response for the client — this is purely a Spring Security framework mechanism.

Quick Comparison Table

ExceptionThrown whenDelegated toTypical response
AuthenticationExceptionNo valid Authentication exists at allAuthenticationEntryPointRedirect to login (web) or 401 + WWW-Authenticate (API)
AccessDeniedExceptionAuthenticated, but lacking the required authorityAccessDeniedHandler403 Forbidden, or a rendered access-denied page
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    http
        .exceptionHandling(exceptions -> exceptions
            .authenticationEntryPoint((request, response, authException) -> {
                response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
                response.setContentType("application/json");
                response.getWriter().write("{\"error\":\"Unauthorized\"}");
            })
            .accessDeniedHandler((request, response, accessDeniedException) -> {
                response.setStatus(HttpServletResponse.SC_FORBIDDEN);
                response.setContentType("application/json");
                response.getWriter().write("{\"error\":\"Forbidden\"}");
            })
        )
        .authorizeHttpRequests(auth -> auth.anyRequest().authenticated());
 
    return http.build();
}

Bonus — what it does NOT handle

ExceptionTranslationFilter only catches AuthenticationException and AccessDeniedException. Business exceptions thrown from your controllers are handled separately by Spring MVC’s @ExceptionHandler and @ControllerAdvice, which run further down the chain, after security has already allowed the request to reach the DispatcherServlet.

Bottom line: Match the exception to its origin: AuthenticationException means “we don’t know you” (401); AccessDeniedException means “we know you, but no” (403).

🎯 Interview Insight: A precise answer pairs each exception with its origin and the correct status code. Getting this pairing right is the whole point of the question.

Q14.  What is @EnableWebSecurity and what does it auto-configure?

Answer – @EnableWebSecurity turns on Spring Security’s web (Servlet Filter-based) infrastructure. Placed on a @Configuration class, it imports WebSecurityConfiguration and HttpSecurityConfiguration via @Import.

@EnableWebSecurity is mandatory — without it, none of your SecurityFilterChain beans are ever connected to incoming requests, no matter how carefully you configure them. It’s the switch that turns a set of bean definitions into an actual, running security layer.

What it wires up:

  • WebSecurityConfiguration — builds and exposes the FilterChainProxy.
  • HttpSecurityConfiguration — supplies the HttpSecurity bean used in your SecurityFilterChain @Bean methods.
  • springSecurityFilterChain — the actual filter registered with the Servlet container via DelegatingFilterProxy.
@Configuration
@EnableWebSecurity                             // activates the web security filter infrastructure
@EnableMethodSecurity(prePostEnabled = true)   // separately activates @PreAuthorize / @PostAuthorize
public class SecurityConfig {
 
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http.authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
            .formLogin(Customizer.withDefaults());
        return http.build();
    }
}

Quick Comparison Table

AnnotationActivatesIndependent of the other?
@EnableWebSecurityHTTP-layer, Servlet Filter-based security modelYes
@EnableMethodSecurity@PreAuthorize / @PostAuthorize on methodsYes

Bottom line: Neither annotation implies the other — an app can use either alone, or both together, depending on whether it needs HTTP-layer rules, method-layer rules, or both.

🎯 Interview Insight: A sharp answer draws the line precisely: @EnableWebSecurity turns on the filter infrastructure itself; @EnableMethodSecurity turns on annotation-driven method authorization. They’re independent switches — confusing the two, or assuming one implies the other, is a common mistake.

Q15.  What is the difference between form login and HTTP Basic authentication?

Answer – Both ultimately verify a username and password, but they differ significantly in transport mechanisms, user experience, and the types of clients they suit. Both are core Spring Security mechanisms, enabled with a single method call each on HttpSecurity — formLogin() and httpBasic().

Quick Comparison Table

AspectForm LoginHTTP Basic
User interfaceHTML login pageBrowser’s native credential prompt
StateTypically session-based (JSESSIONID)Stateless — credentials sent with every request
Credentials on wireSent once, in a POST bodySent Base64-encoded on every request
Best suited forTraditional server-rendered web appsSimple APIs, service-to-service calls, testing
Failure responseRedirect to /login? error401 with WWW-Authenticate header
Requires HTTPSStrongly recommendedMandatory — Base64 is not encryption

Form Login (formLogin()) — for browser apps

Presents an HTML login page (default or custom), where the user submits credentials once as a POST request. On success, a session is created, and a JSESSIONID cookie is issued, keeping the user logged in for subsequent requests. UsernamePasswordAuthenticationFilter processes the login POST.

HTTP Basic (httpBasic()) — simple and stateless

No login form — the client sends an Authorization header, valued “Basic ” + Base64(username: password), on every single request. There is no session and no redirect; a missing or invalid header gets a 401 with a WWW-Authenticate: Basic header.

// Form login — session-based, browser-friendly
http.formLogin(form -> form
        .loginPage("/login")
        .defaultSuccessUrl("/dashboard")
        .permitAll());
 
// HTTP Basic — stateless, credential sent on every request
http.httpBasic(Customizer.withDefaults())
    .sessionManagement(session -> session
        .sessionCreationPolicy(SessionCreationPolicy.STATELESS));
 
// GET /api/orders HTTP/1.1
// Authorization: Basic dXNlcjpwYXNzd29yZA==
//                       ^ Base64("user:password") — NOT encrypted, just encoded

When to Use Which

  • Use Form Login when — you’re building a traditional server-rendered web app with a browser-based UI.
  • Use HTTP Basic when — you need simple internal service-to-service calls, health-check endpoints, or fast prototyping.
  • Use neither, prefer JWT/OAuth2 when — you’re building a production REST API or microservice — covered in the next article.

Bottom line: Neither mechanism is typically used for end-user auth in modern production APIs, but HTTP Basic remains common for internal, low-stakes calls precisely because it needs almost no configuration.

🎯 Interview Insight: A commonly asked follow-up: ‘Is Basic authentication secure?’ Base64 encoding provides zero confidentiality — it’s trivially decodable. Basic auth is only as secure as the transport it runs over, which is why it must always be paired with HTTPS/TLS.

Conclusion

Spring Security’s architecture rests on a small number of consistent ideas repeated throughout the framework: every request flows through an ordered Servlet Filter chain; authentication is delegated through AuthenticationManager to one or more AuthenticationProvider implementations; the resulting Authentication is stored in a thread-local SecurityContext; and authorization decisions consult that same Authentication afterward.

Key takeaways from this article:

  • Spring Security is explicit by design — every rule you want enforced is declared in a SecurityFilterChain, never inferred for you
  • Authentication answers who you are; authorization answers what you can do — enforced by different components at different points in the chain
  • SecurityFilterChain, built with the lambda-based HttpSecurity DSL, is the only supported configuration style in Spring Security 6.x
  • UserDetailsService, PasswordEncoder, AuthenticationManager, and AuthenticationProvider are the core extensibility points for real, database-backed authentication
  • ExceptionTranslationFilter is what turns internal security exceptions into the 401 and 403 responses clients actually see

 

Leave a Comment