The Spring Security questions Java interviews ask, with short answers checked against the current docs and the old answers that now sound out of date.
Most Spring Security interview questions test four things: how a request moves through the filter chain, how authentication produces an Authentication in the SecurityContextHolder, how authorization rules are matched, and when protections like CSRF should stay on. The trap is the version. Many answers online were written for Spring Security 5 and still describe WebSecurityConfigurerAdapter, which was removed in 6.0. Spring Security 7, which pairs with Spring Boot 4, removed more: and() chaining and authorizeRequests. Below: the version changes to know, 14 questions grouped by topic, a worked debugging example, and a short prep plan.
Start with the version, because the right answer depends on it
Spring Boot 3.0 shipped with Spring Security 6.0. The 7.0 migration guide assumes Spring Boot 4.0. Before the interview, check the job post or ask the recruiter which Boot version the team runs, then answer for that version. If you are not sure, describe the current API and mention the old one in a sentence. That shows you know the history without sounding stuck in it.
These are the answers that most often give away out-of-date preparation:
- If your answer says — Say this for 6.x and 7.x — What changed
- "Extend
WebSecurityConfigurerAdapterand overrideconfigure()" — Declare aSecurityFilterChainbean that takesHttpSecurityand returnshttp.build()— Deprecated in 5.7, removed in 6.0 - "
authorizeRequests().antMatchers(...)" —authorizeHttpRequests(auth -> auth.requestMatchers(...))—authorizeRequestswas removed in 7.0;AntPathRequestMatcherandMvcRequestMatcherwere removed in favor ofPathPatternRequestMatcher - "Chain sections with
.and()" — Use the lambda DSL: one lambda per section —and()was removed from theHttpSecurityDSL in 7.0 - "
@EnableGlobalMethodSecurity(prePostEnabled = true)" —@EnableMethodSecurity, which turns on@PreAuthorizeand its siblings by default — The old annotation is deprecated, and in 7.0 it moved to the separatespring-security-accessmodule - "
FilterSecurityInterceptorenforces URL rules" —AuthorizationFilter, backed by anAuthorizationManager— Part of the same move tospring-security-access - "Use the OAuth2 password grant" — Authorization code (with PKCE for public clients) or client credentials — Password grant support was removed in 7.0
Architecture: the filter chain
1. How does Spring Security plug into a servlet application?
Tests: whether you know what happens before your controller runs. The container registers a DelegatingFilterProxy, a standard servlet Filter that hands the work to a Spring bean. That bean is FilterChainProxy, which picks a SecurityFilterChain for the request and runs its filters in a fixed order. FilterChainProxy also clears the SecurityContext after each request and applies the HttpFirewall. A good extra point: if you are debugging, it is the place to set a breakpoint, because every request goes through it.
2. What happens if you define more than one `SecurityFilterChain`?
Only the first chain that matches the request runs. A common setup is one chain for /api/** (stateless, token-based) and a second for everything else (form login, sessions). Put the narrow matcher first. Otherwise the broad chain catches everything and the API chain never runs.
3. What does `ExceptionTranslationFilter` do?
It turns security exceptions into HTTP responses. If the user is not authenticated, or the exception is an AuthenticationException, it clears the context, saves the request so it can be replayed after login, and calls the AuthenticationEntryPoint (a redirect to a login page, or a WWW-Authenticate header). If an authenticated user hits an AccessDeniedException, it calls the AccessDeniedHandler. That split is the difference between a 401-style "log in first" response and a 403 "you are logged in but not allowed".
Authentication
4. Walk me through a username and password login.
Tests: whether you can name the components in order. The login filter builds a UsernamePasswordAuthenticationToken and passes it to the AuthenticationManager, usually a ProviderManager. The ProviderManager asks each AuthenticationProvider in turn. DaoAuthenticationProvider loads the UserDetails from your UserDetailsService, checks the password with the PasswordEncoder, and returns an authenticated token carrying the user's authorities. The filter then stores it in the SecurityContextHolder. If no provider supports the token type, the result is a ProviderNotFoundException.
5. Where is the current user stored, and what happens on another thread?
In the SecurityContextHolder, which by default uses a ThreadLocal. Code on the same thread can read the user without passing it around. A thread you start yourself does not see it unless you change the strategy (for example MODE_INHERITABLETHREADLOCAL) or pass the context along. The docs also advise creating a new SecurityContext rather than calling getContext().setAuthentication(...), to avoid race conditions between threads.
6. I logged a user in by hand and they were logged out on the next request. Why?
This tests whether you know a 6.x change. Current versions use SecurityContextHolderFilter, which only loads the context; unlike the older SecurityContextPersistenceFilter, it does not save it. If your code sets the context itself, it must also call securityContextRepository.saveContext(...). Answers written for older versions often miss this, so it is a good way to show real version knowledge.
7. How should passwords be stored?
With an adaptive one-way function: bcrypt, PBKDF2, scrypt or Argon2, tuned so a check takes about a second on your hardware. The stronger answer names DelegatingPasswordEncoder. It stores hashes as {id}encodedPassword, for example {bcrypt}$2a$10$..., so you can change algorithms later and still verify old hashes. A hash with no prefix fails with "There is no PasswordEncoder mapped for the id \"null\"", an error you will see when migrating an old user table. 7.0 also added encoders based on the Password4j library.
Authorization
8. How are URL rules evaluated, and what should the last rule be?
In the order you declare them, and only the first match applies. The docs recommend ending with anyRequest().denyAll() so that anything you forgot is denied, which turns your rules into an allow list. Authorization also runs on every dispatch, including forwards and error dispatches, which is why the docs' example permits FORWARD and ERROR explicitly.
9. What is the difference between `hasRole` and `hasAuthority`?
hasAuthority("ROLE_ADMIN") checks the exact string. hasRole("ADMIN") is a shortcut that adds the ROLE_ prefix for you. This matters with JWTs: the resource server maps each scope to an authority prefixed SCOPE_, so hasRole("admin") will never match a token's admin scope. Either check hasAuthority("SCOPE_admin") or map the claim yourself with a JwtAuthenticationConverter.
10. `@PreAuthorize` or `@Secured`?
@PreAuthorize takes a SpEL expression, so it can check arguments as well as roles, for example #id == authentication.name. @PostAuthorize checks the return value, and @PreFilter and @PostFilter filter collections. The docs call @Secured a legacy option that @PreAuthorize supersedes, and @EnableMethodSecurity only enables it if you set securedEnabled = true. Two follow-ups interviewers like. First, the Spring Boot security starter does not turn on method security; you add @EnableMethodSecurity. Second, method security works through Spring AOP proxies, so a call from one method to another on the same object (this.delete()) skips the check.
Web protections
11. When is it fine to disable CSRF protection?
CSRF protection is on by default for unsafe methods such as POST. The docs recommend it for any request a normal user's browser could make, and say a service used only by non-browser clients likely wants it off. The reasoning is what earns the marks: the attack works because the browser attaches credentials automatically, such as a session cookie or Basic auth. So "it's stateless" is not enough on its own; the docs point out that an app keeping its auth state in a custom cookie is still exposed. An API used only by non-browser clients, where credentials such as a bearer token are sent explicitly in a header rather than attached automatically by the browser, is the case where turning it off makes sense. For a single-page app whose JavaScript reads the CSRF token from a cookie, 7.0 added csrf.spa() as a ready-made configuration. "Disable it for REST" with no reason is a weak answer.
12. How does Spring Security handle sessions?
On login it changes the session ID by default (changeSessionId), which protects against session fixation. maximumSessions(1) ends the older session when the user logs in again; add maxSessionsPreventsLogin(true) to reject the new login instead. SessionCreationPolicy.STATELESS means no session is created, and the security context is not stored between requests. For remember-me, the simple hash-based cookie can be reused by anyone who captures it until it expires; the docs suggest the persistent-token approach when you need more security.
Tokens, OAuth2 and newer features
13. How do you secure a REST API with JWTs?
A hand-written filter that parses tokens with a library like jjwt is one way to do it, but the built-in route is the OAuth2 resource server: add the dependency, set spring.security.oauth2.resourceserver.jwt.issuer-uri, and configure oauth2ResourceServer(oauth2 -> oauth2.jwt(...)). On the first request it discovers the issuer's public keys. It then checks each token's signature, its exp and nbf times and its iss claim, and maps scopes to SCOPE_ authorities. Mentioning this, and then explaining when a custom filter would still make sense, beats reciting a hand-written filter. For browser logins through Google or another provider, OAuth2 Login uses the authorization code grant; the client turns on PKCE automatically for public clients that have no secret. Spring Authorization Server, for running your own token issuer, became part of Spring Security in 7.0.
14. How would you add multi-factor authentication?
Spring Security 7.0 added built-in MFA. Each login mechanism adds a factor authority to the Authentication (for example FACTOR_PASSWORD after a password login). @EnableMultiFactorAuthentication(authorities = {...}) then requires all the listed factors on every authorization rule. In the docs' example, a user who has only entered a password is sent to the one-time-token login to complete the second factor. MFA support arrived in 7.0, so if the team is on 6.x, say that you are describing the newer version.
How do you test security rules? Not a numbered question, but expect it as a follow-up. @WithMockUser runs a test as a user named "user" with ROLE_USER, and that user does not need to exist. @WithUserDetails loads a real user from your UserDetailsService. With MockMvc, a POST needs .with(csrf()), and .with(csrf().useInvalidToken()) checks that a bad token is rejected.
Worked example: "Why does this endpoint return 403?"
Debugging questions show more than definitions. Here is a configuration for an API, followed by four problems an interviewer might plant in it. The code is illustrative, written for Spring Security 6.x/7.x.
@Configuration
@EnableWebSecurity
@EnableMethodSecurity
public class ApiSecurityConfig {
@Bean
SecurityFilterChain api(HttpSecurity http) throws Exception {
http
.securityMatcher("/api/**")
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/public/**").permitAll()
.requestMatchers("/api/admin/**").hasAuthority("SCOPE_admin")
.anyRequest().authenticated()
)
.sessionManagement(s -> s.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.csrf(csrf -> csrf.disable()) // only because this API serves non-browser clients with bearer tokens
.oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults()));
return http.build();
}
}- Admins get 403 on `/api/admin/
.** Someone changed the rule tohasRole("ADMIN"). Tokens carrySCOPE_authorities, so there is noROLE_ADMIN. Fix the rule or map the claim with aJwtAuthenticationConverter`. - Any logged-in user can reach admin endpoints. A rule such as
requestMatchers("/api/**").authenticated()was added above the admin rule. The first match wins, so the admin rule never runs. For any caller with a valid token, this bug returns 200, not an error, which is why it is dangerous. - A form POST in the browser-facing chain returns 403. That chain keeps CSRF on, as it should, and the form does not send the token. The fix is to send the token, not to turn CSRF off.
- `@PreAuthorize` on a service method never blocks anyone. Either
@EnableMethodSecurityis missing, or the method is called from inside the same class, which skips the proxy.
How to diagnose it, which is the part interviewers want to hear: Spring Security deliberately leaves the reason for a rejection out of the response, so you look at logs. The filter list prints at DEBUG level on startup. Setting logging.level.org.springframework.security=TRACE shows each filter as it runs and the reason a request was rejected, such as an invalid CSRF token.
A five-day prep plan
- Day 1: Find out which Spring Boot version the team uses. Read the servlet architecture page of the Spring Security reference, then draw the filter chain from memory.
- Day 2: Build a small Boot app with form login, an in-memory user, and a
DelegatingPasswordEncoder. Turn on TRACE logging and read a full login. - Day 3: Add a second, stateless chain for
/api/**with the resource server, and secure one method with@PreAuthorize. Break it in the four ways above and fix each one. - Day 4: Write tests with
@WithMockUserand MockMvc, including a CSRF failure. If the role uses OAuth2 login or Spring Authorization Server, wire up one provider. - Day 5: Practise saying the answers out loud, 60 to 90 seconds each, and prepare one story about a security problem you solved on a real project, so you have an answer ready for "where have you used this?"
For the rest of a Java backend loop, see our guides to Java coding interview questions, Java architect interview questions and the technical round.
If you want help during the interview itself, Verve AI's Interview Copilot listens to the conversation and shows suggestions that only you can see. It has a Software Engineering domain, and you can upload your resume and add documents such as project notes to its Knowledge Base, so answers draw on the documents you enable.
Sources
- DelegatingFilterProxy, FilterChainProxy, first matching chain, filter order, ExceptionTranslationFilter, DEBUG/TRACE logging, no rejection detail in responses: https://docs.spring.io/spring-security/reference/servlet/architecture.html
- SecurityContextHolder and ThreadLocal, new SecurityContext advice, ProviderManager, ProviderNotFoundException, AuthenticationEntryPoint: https://docs.spring.io/spring-security/reference/servlet/authentication/architecture.html
- DaoAuthenticationProvider flow: https://docs.spring.io/spring-security/reference/servlet/authentication/passwords/dao-authentication-provider.html
- SecurityContextHolderFilter and explicit save: https://docs.spring.io/spring-security/reference/servlet/authentication/persistence.html
- Password storage, DelegatingPasswordEncoder, {id} format, "id null" error, one-second tuning, Password4j encoders: https://docs.spring.io/spring-security/reference/features/authentication/password-storage.html
- authorizeHttpRequests, first match wins, denyAll as an allow list, every dispatch is authorized: https://docs.spring.io/spring-security/reference/servlet/authorization/authorize-http-requests.html
- @EnableMethodSecurity defaults, @Secured as legacy, Boot starter does not enable method security, hasRole prefix, AOP flow: https://docs.spring.io/spring-security/reference/servlet/authorization/method-security.html
- Self-invocation bypasses proxy advice: https://docs.spring.io/spring-framework/reference/core/aop/proxying.html
- CSRF defaults, disabling for backends without browser traffic, csrf.spa(): https://docs.spring.io/spring-security/reference/servlet/exploits/csrf.html
- When to use CSRF protection, why browsers make it work, stateless apps with custom cookies: https://docs.spring.io/spring-security/reference/features/exploits/csrf.html
- Session fixation default, concurrent sessions, STATELESS: https://docs.spring.io/spring-security/reference/servlet/authentication/session-management.html
- Remember-me approaches and the captured-token caveat: https://docs.spring.io/spring-security/reference/servlet/authentication/rememberme.html
- Resource server JWT setup, validation steps, SCOPE_ prefix, JwtAuthenticationConverter: https://docs.spring.io/spring-security/reference/servlet/oauth2/resource-server/jwt.html
- OAuth2 Login with the authorization code grant: https://docs.spring.io/spring-security/reference/servlet/oauth2/login/core.html
- PKCE used automatically for public clients: https://docs.spring.io/spring-security/reference/servlet/oauth2/client/authorization-grants.html
- MFA with FactorGrantedAuthority and @EnableMultiFactorAuthentication: https://docs.spring.io/spring-security/reference/servlet/authentication/mfa.html
- @WithMockUser, @WithUserDetails: https://docs.spring.io/spring-security/reference/servlet/test/method.html
- with(csrf()) and useInvalidToken(): https://docs.spring.io/spring-security/reference/servlet/test/mockmvc/csrf.html
- 7.0 changes (and() and authorizeRequests removed, Ant/Mvc matchers removed, Access API moved, MFA added, Authorization Server merged, password grant removed, csrf.spa(), Password4j): https://docs.spring.io/spring-security/reference/7.0/whats-new.html
- 7.0 migration assumes Spring Boot 4.0: https://docs.spring.io/spring-security/reference/migration/index.html
- Lambda DSL required in 7: https://docs.spring.io/spring-security/reference/6.5/migration-7/configuration.html
- WebSecurityConfigurerAdapter deprecated in 5.7 and the SecurityFilterChain replacement: https://spring.io/blog/2022/02/21/spring-security-without-the-websecurityconfigureradapter
- WebSecurityConfigurerAdapter removed for 6.0: https://github.com/spring-projects/spring-security/issues/10902
- Access API (including FilterSecurityInterceptor and @EnableGlobalMethodSecurity) moved to spring-security-access: https://spring.io/blog/2025/09/09/access-api-moves-to-spring-security-access
- Spring Authorization Server moving into Spring Security 7.0: https://spring.io/blog/2025/09/11/spring-authorization-server-moving-to-spring-security-7-0
- Spring Boot 3.0 uses Spring Security 6.0: https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-3.0-Migration-Guide
- Interview Copilot: https://docs.vervecopilot.com/features/interview-copilot
- Knowledge Base: https://docs.vervecopilot.com/features/knowledge-base
Ryan, CEO of Verve AI
Interview Questions








