Spring Security Interview Questions
A. Spring Security Fundamentals Spring Security is a powerful security framework used to secure Spring applications. Spring Security is mainly used for: Authentication (who the user is)Authorization (whatβ¦
A. Spring Security Fundamentals
Expand a question to read the answer. Tick when youβre prepared (progress saved on this device).
Spring Security is a powerful security framework used to secure Spring applications.
Spring Security is mainly used for:
- Authentication (who the user is)
- Authorization (what the user can access)
- Protecting APIs, web applications, and microservices
- Handling security concerns like CSRF, CORS, session management, password encoding
It follows the principle of βsecure by defaultβ and integrates seamlessly with Spring Boot.
Authentication
- Verifies who the user is
- Example: Validating username and password during login
Authorization
- Verifies what the authenticated user is allowed to do
- Example: Checking if a user has
ROLE_ADMINto access/admin
Authentication happens first, authorization happens after authentication.
Core components of Spring Security are:
- Security Filter Chain β Intercepts HTTP requests
- AuthenticationManager β Performs authentication
- AuthenticationProvider β Contains authentication logic
- UserDetailsService β Loads user data
- PasswordEncoder β Encrypts and validates passwords
- SecurityContextHolder β Stores authenticated user details
These components work together to enforce security.
The Spring Security Filter Chain is a series of security filters that intercept every HTTP request before it reaches the controller.
Responsibilities of Spring Security Filter Chain :
- Extract credentials from request
- Authenticate the user
- Check authorization rules
- Handle security exceptions
Filters like UsernamePasswordAuthenticationFilter and AuthorizationFilter are part of this chain.
When a request comes to your Spring application:
Client β Security Filters β Controller
Spring Security applies multiple filters one by one, such as:
- Authentication
- Authorization
- CSRF protection
- Session handling
If any filter fails, the request is stopped immediately
Spring Security works in Spring Boot by using a filter chain to authenticate users and authorize requests before controllers are executed.
Overall Flow of Spring Security
Client Request
β
Spring Security Filter Chain
β
Authentication
β
Authorization
β
Controller
Step-by-Step Working
1. Application Starts
- Spring Boot auto-configures Spring Security
- By default: All endpoints are secured A login form is enabled A default user is created
2. Request Comes In
- Every HTTP request passes through the Security Filter Chain
- (before reaching
@Controlleror@RestController).
3. Authentication (Who are you?)
Spring Security checks identity using:
- Username & password (form login)
- JWT token
- OAuth2 / LDAP (if configured)
If valid β user is authenticated
If invalid β 401 Unauthorized
4. Authorization (What can you access?)
Spring checks:
- User roles (
ROLE_USER,ROLE_ADMIN) - Permissions
Example:
.requestMatchers("/admin/**").hasRole("ADMIN")
If no permission β 403 Forbidden
5. Request Reaches Controller
If both authentication & authorization succeed:
Controller method is executed
How You Configure It (Spring Boot Way)
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/public/**").permitAll()
.anyRequest().authenticated()
)
.formLogin()
.httpBasic();
return http.build();
}
This defines:
- How login happens
- Which URLs are public
- Which need login
In Spring Security, GrantedAuthority represents a permission, while a Role is a predefined authority (prefixed with ROLE_) used to group permissions in Spring Security.
GrantedAuthority
- Represents a permission or authority
- Example:
READ_PRIVILEGE,WRITE_PRIVILEGE
Roles
- A special type of authority prefixed with
ROLE_ - Example:
ROLE_ADMIN,ROLE_USER
Internally, roles are treated as authorities.
SecurityContextHolder stores security information of the current user.
It contains:
- Authentication object
- User details
- Granted authorities
It allows access to the currently logged-in user from anywhere in the application.
UserDetailsService is used to load user-specific data during authentication.
Key method:
UserDetails loadUserByUsername(String username)
It typically:
- Fetches user from database
- Returns username, password, and roles
It connects Spring Security with your custom user data source.
AuthenticationManager is the central interface responsible for authentication.
- It delegates authentication to one or more
AuthenticationProviders - Returns an authenticated
Authenticationobject on success - Throws exception on failure
Think of it as the entry point of authentication logic.
PasswordEncoder is used to hash and verify passwords.
Why itβs important:
- Prevents storing plain-text passwords
- Protects against data breaches
- Supports secure algorithms like BCrypt
Example:
passwordEncoder.matches(rawPassword, encodedPassword);
Storing plain passwords is a major security risk and should never be done.
B. Spring Security Configuration
Expand a question to read the answer. Tick when youβre prepared (progress saved on this device).
In Spring Boot, Spring Security is configured using a SecurityFilterChain bean.
Basic steps:
- Add
spring-boot-starter-security - Define a
SecurityFilterChain - Configure authorization rules, login, logout, CSRF, sessions
Example:
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/public/**").permitAll()
.anyRequest().authenticated()
)
.formLogin()
.logout();
return http.build();
}
Spring Boot auto-configures security, but this allows custom control.
WebSecurityConfigurerAdapter was the old way of configuring Spring Security (before Spring Security 5.7).
Problems:
- Heavy inheritance-based design
- Less flexible
- Harder to test
Replacement (Current Approach):
- Use SecurityFilterChain
- Configuration through beans + lambdas
The new approach is component-based, cleaner, and recommended.
You secure URLs using request matchers in HttpSecurity.
Example:
http.authorizeHttpRequests(auth -> auth
.requestMatchers("/admin/**").hasRole("ADMIN")
.requestMatchers("/user/**").hasAnyRole("USER", "ADMIN")
.anyRequest().authenticated()
);
Security rules are applied top to bottom.
Public routes β permitAll()
Secured routes β authenticated() or hasRole()
Example:
.requestMatchers("/login", "/register").permitAll()
.anyRequest().authenticated()
Public routes are accessible without login.
Method-level security is enabled using @EnableMethodSecurity and enforced with annotations like @PreAuthorize, @PostAuthorize, @Secured, and @RolesAllowed.
Enable Method-Level Security
@EnableMethodSecurity // Spring Security 6+
(Older versions use @EnableGlobalMethodSecurity)
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
Common Method-Level Security Annotations
@RolesAllowedβ JSR-250 standard role check@PreAuthorizeβ Checks access before method execution@PostAuthorizeβ Checks access after method execution@Securedβ Role-based access (ROLE_prefix required)
This allows fine-grained access control at method level.
@PreAuthorize
- Most powerful
- Supports SpEL expressions
@PreAuthorize("hasRole('ADMIN')")
@Secured
- Simple role-based check
- Spring-specific
@Secured("ROLE_ADMIN")
@RolesAllowed
- Java standard (JSR-250)
- Cleaner and portable
@RolesAllowed("ADMIN")
@PreAuthorize is preferred for real-world applications.
Login and logout URLs are customized in Spring Security using formLogin() and logout() configuration inside SecurityFilterChain.
Below is a simple and clear explanation
1. Customize Login URL
Example
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/login", "/public/**").permitAll()
.anyRequest().authenticated()
)
.formLogin(form -> form
.loginPage("/login") // custom login page URL
.loginProcessingUrl("/doLogin") // URL where login form is submitted
.defaultSuccessUrl("/home", true)
.failureUrl("/login?error=true")
);
return http.build();
}
What this means
/loginβ your custom login page/doLoginβ Spring Security handles authentication here/homeβ redirect after successful login/login?error=trueβ redirect if login fails
2. Customize Logout URL
Example
.logout(logout -> logout
.logoutUrl("/doLogout") // custom logout URL
.logoutSuccessUrl("/login?logout=true")
.invalidateHttpSession(true)
.deleteCookies("JSESSIONID")
)
What this means
/doLogoutβ logout request URL- Session is destroyed
- User redirected to login page
3. Login Form Example (HTML)
<form action="/doLogin" method="post">
<input type="text" name="username" />
<input type="password" name="password" />
<button type="submit">Login</button>
</form>
Important:
Field names must be:
usernamepassword
(or explicitly configured)
4. Customize Parameter Names
.formLogin(form -> form
.usernameParameter("email")
.passwordParameter("pwd")
)
In Spring Security, session management is configured using SessionCreationPolicy. You can choose stateful (session-based) or stateless (token-based) authentication.
1. Stateful Session Management (Default)
When to use
- Form login
- Server-side sessions
- Traditional web applications
Configuration
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.sessionManagement(session -> session
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
)
.formLogin();
return http.build();
}
How it works
- Session is created on login
- Session ID stored in JSESSIONID cookie
- Server keeps user state
Default behavior in Spring Security
2. Stateless Session Management (JWT / REST APIs)
When to use
- REST APIs
- JWT authentication
- Microservices
Configuration
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable())
.sessionManagement(session -> session
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
);
return http.build();
}
How it works
- No HTTP session is created
- Every request must carry token (JWT)
- Server does not store user state
Best for scalability
CSRF can be disabled in Spring Security by configuring HttpSecurity to disable CSRF protection, and it is appropriate mainly for stateless APIs (REST APIs) that use token-based authentication like JWT. It can be disable using http.csrf().disable() and is appropriate only for stateless, token-based APIs, not for session-based web applications.
How to Disable CSRF
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(auth -> auth.anyRequest().authenticated());
return http.build();
}
When to disable CSRF:
- Stateless REST APIs
- JWT-based authentication
- APIs consumed by mobile or frontend apps
When NOT to disable:
- Traditional web apps with forms & sessions
CSRF protection is mainly for browser-based session apps.
To secure a REST API with Spring Security, you usually use token-based (stateless) authentication, most commonly JWT, along with proper authorization rules.
Below is a clear, explanation
1. Choose Stateless Security (REST APIs)
REST APIs should be stateless (no server session).
http.sessionManagement(session ->
session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
);
No HTTP session
Every request is independent
2. Disable CSRF (for APIs)
CSRF is for browser-based sessions, not APIs.
http.csrf(csrf -> csrf.disable());
3. Authenticate Using JWT (Recommended)
Flow
- User sends username & password to
/login - Server validates credentials
- Server returns JWT token
- Client sends token in every request:
Authorization: Bearer <JWT>
4. Add JWT Authentication Filter
http.addFilterBefore(jwtFilter,
UsernamePasswordAuthenticationFilter.class);
Extracts token
Validates token
Sets user in SecurityContext
5. Secure Endpoints (Authorization)
http.authorizeHttpRequests(auth -> auth
.requestMatchers("/auth/**").permitAll()
.requestMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
);
6. Configure Authentication Provider
@Bean
AuthenticationManager authenticationManager(
AuthenticationConfiguration config) throws Exception {
return config.getAuthenticationManager();
}
Uses:
UserDetailsServicePasswordEncoder
7. Handle Unauthorized Access
- 401 Unauthorized β Not logged in
- 403 Forbidden β No permission
(Optional)
http.exceptionHandling(ex -> ex
.authenticationEntryPoint(customEntryPoint)
);
8. Secure API at Method Level (Optional)
@PreAuthorize("hasRole('ADMIN')")
@GetMapping("/users")
public List<User> getUsers() { }
C. Spring Security Mechanisms
Expand a question to read the answer. Tick when youβre prepared (progress saved on this device).
Basic Authentication is a way to secure an application where the username and password are sent with every HTTP request, encoded using Base64. It is simple but less secure if not used with HTTPS.
How Basic Authentication Works
1. Client sends a request with header:
Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=
2. Server:
- Decodes Base64
- Extracts
username:password - Verifies credentials
3. If valid β request is allowed
If invalid β 401 Unauthorized
Enable Basic Authentication in Spring Security
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.anyRequest().authenticated()
)
.httpBasic();
return http.build();
}
When to Use Basic Authentication
Internal services
Testing APIs
Simple admin tools
Not recommended for public applications
Form-Based Authentication is a login mechanism where a user submits credentials through an HTML form, and Spring Security authenticates the user and creates a session.
How It Works (Step-by-Step)
1. User Requests a Protected Page
- User tries to access
/dashboard - Not authenticated
Spring Security redirects to login page
2. User Submits Login Form
Login form sends data:
POST /login
username=admin
password=admin123
(Default URL is /login)
3. Spring Security Authenticates
UsernamePasswordAuthenticationFilter: Reads username & password CallsAuthenticationManagerVerifies credentials usingUserDetailsService
If valid β authentication success
If invalid β authentication failure
4. Session Is Created
- Spring Security creates an HTTP session
- Stores user details in SecurityContext
- Session ID sent as
JSESSIONIDcookie
5. Access Granted
- User is redirected to requested page
- Future requests use session (no re-login)
Configuration Example (Spring Boot)
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/login").permitAll()
.anyRequest().authenticated()
)
.formLogin(form -> form
.loginPage("/login")
.defaultSuccessUrl("/home")
.failureUrl("/login?error=true")
)
.logout();
return http.build();
}
Login Form Example
<form action="/login" method="post">
<input name="username" />
<input name="password" type="password" />
<button type="submit">Login</button>
</form>
JWT (JSON Web Token) is a stateless, self-contained token used for authentication.
Structure:
- Header β algorithm & token type
- Payload β user details & claims
- Signature β verifies token integrity
Flow:
- User logs in
- Server generates JWT
- Client sends JWT in
Authorization: Bearer <token> - Server validates token for each request
JWT removes the need for server-side sessions.
JWT authentication is a stateless security mechanism where the server does not store sessions. Instead, the client sends a JWT token with every request to prove identity.
Overall JWT Flow
Client β Login β JWT Generated
Client β Sends JWT with each request
Server β Validates JWT β Allows access
Step-by-Step JWT Authentication Flow
1. User Login (Authentication)
The client sends credentials to a login endpoint.
POST /auth/login
{
"username": "user",
"password": "pass"
}
2. Spring Security Authenticates User
AuthenticationManagerverifies username & password- Uses:
UserDetailsServicePasswordEncoder
If valid β authentication success
If invalid β 401 Unauthorized
3. JWT Token Is Generated
After successful authentication:
- Server creates a JWT
- Token contains: Username Roles Expiration time Signature
Example response:
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
4. Client Stores JWT
Client stores token:
- Frontend memory
- LocalStorage / SessionStorage (careful)
- Mobile secure storage
JWT is NOT stored on server
5. Client Sends JWT with Every Request
Each secured request includes:
Authorization: Bearer <JWT_TOKEN>
6. JWT Filter Intercepts Request
A custom JWT filter runs before authentication filter.
What it does:
- Extracts token from header
- Validates token (signature & expiry)
- Loads user details
- Sets authentication in
SecurityContext
User is now authenticated
Invalid token β request rejected
7. Authorization Happens
Spring Security checks:
- Roles
- Permissions
.hasRole("ADMIN")
If allowed β controller is executed
If not β 403 Forbidden
8. Stateless Session Configuration
http
.csrf(csrf -> csrf.disable())
.sessionManagement(session ->
session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
);
No HTTP session
Scalable & cloud-friendly
Minimal JWT Security Configuration
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable())
.sessionManagement(session ->
session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
)
.authorizeHttpRequests(auth -> auth
.requestMatchers("/auth/**").permitAll()
.anyRequest().authenticated()
)
.addFilterBefore(jwtFilter,
UsernamePasswordAuthenticationFilter.class);
return http.build();
}
OAuth2 is an authorization framework that allows third-party apps to access user data without sharing passwords.
Spring Security supports OAuth2 via:
- OAuth2 Login (SSO)
- OAuth2 Client
- OAuth2 Resource Server
- JWT validation
Common providers: Google, GitHub, Facebook
Authorization Code Grant is the most secure OAuth2 flow.
Flow:
- User is redirected to Authorization Server
- User logs in & grants permission
- Authorization server returns authorization code
- Client exchanges code for access token
- Access token is used to call APIs
Used in web apps and mobile apps.
In Spring Boot, OAuth2 login is enabled by:
- Adding OAuth2 dependency
- Configuring client details
- Enabling OAuth2 login
Configuration:
http.oauth2Login();
Properties:
spring.security.oauth2.client.registration.google.client-id=
spring.security.oauth2.client.registration.google.client-secret=
Enables Single Sign-On (SSO) with minimal configuration.
LDAP Authentication validates users against a central directory server.
Key points:
- Common in enterprises
- Centralized user management
- Used with Active Directory
Flow:
- User credentials sent to LDAP server
- LDAP validates credentials
- Access is granted based on directory roles
Useful for enterprise internal systems.
SAML integration is a way to enable Single Sign-On (SSO) where a user logs in once with a trusted identity provider, and then can access multiple applications without logging in again.
It uses SAML (Security Assertion Markup Language) to securely exchange authentication information.
Remember-Me authentication is a Spring Security feature that keeps a user logged in even after the browser is closed, so they donβt need to log in again next time.
It works by storing a remember-me token in a cookie.
How Remember-Me Works (Easy Steps)
- User logs in and checks βRemember Meβ
- Spring Security creates a remember-me token
- Token is saved in a browser cookie
- Browser is closed
- On next visit: Cookie is sent to server Token is verified User is logged in automatically
Two Types of Remember-Me
1. Simple Hash-Based Remember-Me
- Token stored only in cookie
- Easier to configure
- Less secure
2. Persistent Token Remember-Me (Recommended)
- Token stored in database + cookie
- More secure
- Token can be invalidated
D. Spring Security Advanced Concepts
Expand a question to read the answer. Tick when youβre prepared (progress saved on this device).
A custom authentication filter in Spring Security is created when you want to handle authentication in your own way (for example: JWT, API key, custom headers, OTP, etc.), instead of using the default login mechanisms.
AuthenticationProvider contains the actual authentication logic.
Responsibilities:
- Validates credentials
- Loads user details
- Returns authenticated
Authenticationobject
Flow of AuthenticationProvider:
Filter β AuthenticationManager β AuthenticationProvider
You can create multiple providers (DB, LDAP, JWT) in one application.
In Spring Security, exceptions are handled by security exception handlers that return proper HTTP responses before the request reaches the controller.
Two Main Security Exceptions
1. AuthenticationException β 401 Unauthorized
- Thrown when the user is not authenticated (invalid credentials, missing token).
- Handled by
AuthenticationEntryPoint
2. AccessDeniedException β 403 Forbidden
- Thrown when the user is authenticated but lacks permission.
- Handled by
AccessDeniedHandler
How to Configure Exception Handling
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.exceptionHandling(ex -> ex
.authenticationEntryPoint(customAuthEntryPoint)
.accessDeniedHandler(customAccessDeniedHandler)
);
return http.build();
}
This is critical for REST APIs to return proper HTTP status codes.
Common Status Codes
| Situation | Status |
|---|---|
| Not logged in | 401 |
| Invalid token | 401 |
| No permission | 403 |
| CSRF failure | 403 |
CORS (Cross-Origin Resource Sharing) is a browser security rule that controls which domains are allowed to access your backend API.
It prevents unauthorized websites from calling your APIs.
Why CORS Is Needed?
- Frontend runs on:
http://localhost:3000 - Backend API runs on:
http://localhost:8080
Because the origin is different, the browser blocks the request unless CORS is configured.
Common CORS Headers
| Header | Meaning |
|---|---|
Access-Control-Allow-Origin | Allowed domains |
Access-Control-Allow-Methods | GET, POST, PUT, DELETE |
Access-Control-Allow-Headers | Authorization, Content-Type |
Access-Control-Allow-Credentials | Cookies / auth |
1. Configure CORS in Spring Boot (Global β Recommended)
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("http://localhost:3000")
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowedHeaders("*")
.allowCredentials(true);
}
}
Works for all controllers
Clean and simple
2. Configure CORS in Spring Security (Very Important)
If Spring Security is used, you must enable CORS there too:
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.cors()
.and()
.csrf(csrf -> csrf.disable());
return http.build();
}
3. Configure CORS at Controller Level (Small Apps)
@CrossOrigin(origins = "http://localhost:3000")
@RestController
public class UserController {
}
Not recommended for large apps
Session fixation is a security attack where an attacker forces a user to use a known session ID.
Spring Security protection:
- Creates a new session after login
- Invalidates old session
Default behavior:
sessionFixation().migrateSession()
Prevents attackers from hijacking authenticated sessions.
Best practices:
- Use JWT or OAuth2
- Stateless authentication
- Centralized authorization server
- Role/claim-based access
Typical setup:
- API Gateway validates token
- Microservices trust token
- Use OAuth2 Resource Server
Never use session-based security in microservices.
Access Control Lists (ACLs) are a fine-grained authorization mechanism that define who can access or modify a specific object and what actions they are allowed to perform.
Instead of just roles, ACLs control permissions at the object level.
Easy Example
Imagine a document system:
| User | Document | Permission |
|---|---|---|
| Alice | File-A | READ, WRITE |
| Bob | File-A | READ |
| Charlie | File-A | NO ACCESS |
Here, access is decided per object, not just by role.
Common ACL Permissions
- READ
- WRITE
- DELETE
- ADMIN
Common approaches:
@WithMockUser@WithAnonymousUser@SpringBootTestMockMvc
Example:
@WithMockUser(roles = "ADMIN")
@Test
void adminAccessTest() { }
Ensures authorization rules work as expected.
Stateful Authentication
- Uses server-side sessions
- Session stored on server
- Common in form-login apps
Stateless Authentication
- Each request is self-contained
- No session stored
- Token-based (JWT)
REST APIs and microservices must be stateless.
Spring Security protects against:
- CSRF (Cross-Site Request Forgery)
- XSS (Cross-Site Scripting)
- Session Fixation
- Clickjacking
- Brute-force attacks
- Unauthorized access
Security headers and filters are enabled by default.
E. Spring Security Real-World / Scenario Based
Expand a question to read the answer. Tick when youβre prepared (progress saved on this device).
Rate limiting and throttling are techniques used to control how many requests a client can make to an API within a given time, to prevent abuse and protect system resources.
- Rate limiting β hard limit (requests beyond limit are rejected)
- Throttling β slows down requests when limit is near (soft control)
In production, rate limiting is best handled at the API Gateway or Redis level rather than inside controllers.
Common Strategies
| Strategy | Meaning |
|---|---|
| Fixed Window | X requests per minute |
| Sliding Window | Smooth rate control |
| Token Bucket | Tokens refilled over time |
| Leaky Bucket | Requests processed at fixed rate |
1. Rate Limiting Using Filter / Interceptor (Spring Boot)
Simple Filter-Based Approach
@Component
public class RateLimitFilter extends OncePerRequestFilter {
private final Map<String, Integer> requestCount = new ConcurrentHashMap<>();
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain)
throws ServletException, IOException {
String ip = request.getRemoteAddr();
requestCount.put(ip, requestCount.getOrDefault(ip, 0) + 1);
if (requestCount.get(ip) > 100) { // limit
response.setStatus(429);
response.getWriter().write("Too many requests");
return;
}
filterChain.doFilter(request, response);
}
}
Simple but not production-ready (no expiry, memory issue)
2. Rate Limiting with Bucket4j (Recommended)
Add Dependency
<dependency>
<groupId>com.github.vladimir-bukhtoyarov</groupId>
<artifactId>bucket4j-core</artifactId>
</dependency>
Token Bucket Example
Bucket bucket = Bucket.builder()
.addLimit(Bandwidth.simple(100, Duration.ofMinutes(1)))
.build();
if (bucket.tryConsume(1)) {
// allow request
} else {
// reject with 429
}
Thread-safe
Accurate
Production-ready
3. Rate Limiting with Redis (Distributed Systems)
Used in:
- Microservices
- Multiple instances
- Cloud deployments
Flow
Client β API Gateway β Redis Counter β API
Shared limit across instances
High scalability
4. API Gateway Level (Best Practice)
If using:
- Spring Cloud Gateway
- NGINX
- Kong
- AWS API Gateway
Example (Spring Cloud Gateway):
filters:
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 10
redis-rate-limiter.burstCapacity: 20
Centralized
No app code needed
Industry standard
Role hierarchy allows higher roles to inherit permissions of lower roles.
Example:
ROLE_ADMIN > ROLE_MANAGER > ROLE_USER
In Spring Boot, HTTPS is enabled by:
Steps:
- Generate or obtain SSL certificate
- Configure keystore
- Enable HTTPS port
Configuration:
server.port=8443server.ssl.enabled=trueserver.ssl.key-store=classpath:keystore.p12
In reactive applications using Spring WebFlux, security is non-blocking and asynchronous. Spring Security uses SecurityWebFilterChain instead of servlet filters and stores authentication details in the reactive context instead of ThreadLocal. Authentication and authorization are performed using reactive components like ReactiveAuthenticationManager, making it scalable and suitable for high-performance microservices and streaming applications.
Core components used in WebFlux security
SecurityWebFilterChainβ reactive equivalent of filter chainServerHttpSecurityβ used to configure securityReactiveAuthenticationManagerβ performs authenticationReactiveUserDetailsServiceβ loads users reactivelyReactiveSecurityContextHolderβ accesses current user
Example configuration:
@Bean
SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
return http
.csrf(ServerHttpSecurity.CsrfSpec::disable)
.authorizeExchange(ex -> ex
.pathMatchers("/public/**").permitAll()
.anyExchange().authenticated()
)
.httpBasic()
.build();
}
Integrating LDAP + JWT combines centralized authentication with stateless authorization, which is very common in enterprise and microservice architectures.
High-level idea
- LDAP β used only for authentication (username/password validation)
- JWT β used for authorization on every request (stateless)
LDAP is not called on every request, only during login.
Authentication flow (step-by-step)
Login phase (LDAP-based):
- User sends username & password
- Spring Security authenticates credentials against LDAP
- If LDAP authentication succeeds: Fetch user roles / groups Generate JWT
Post-login (JWT-based):
- JWT is returned to client
- Client sends JWT in
Authorization: Bearer <token> - Every request: JWT is validated Roles are extracted from token Access is granted or denied
LDAP is only used once, JWT handles everything after.
JWT tokens are refreshed securely by using short-lived access tokens and long-lived refresh tokens. When the access token expires, the client sends the refresh token to a dedicated endpoint. The server validates the refresh token, issues a new access token, and usually rotates the refresh token to prevent reuse. Refresh tokens are stored securely on the server and client, and access tokens remain stateless. This approach minimizes security risks while maintaining scalability.
Core principle (important to say in interview)
- Access Token β short-lived (minutes)
- Refresh Token β longer-lived (days/weeks)
- Never make access tokens long-lived
This reduces damage if a token is compromised.
Standard secure JWT refresh flow
- User logs in
- Server issues: Short-lived access token Long-lived refresh token
- Client uses access token for API calls
- When access token expires:
Client sends refresh token to
/auth/refresh - Server: Validates refresh token Issues a new access token Optionally issues a new refresh token
- Old access token becomes useless
No re-authentication is required.
Multi-Factor Authentication (MFA) strengthens security by requiring two or more independent factors to verify a userβs identity, instead of relying only on a password.
Multi-factor authentication is implemented by adding an additional verification step after primary authentication. In Spring Security, the user first authenticates with username and password, then completes a second factor such as OTP or TOTP. Only after the second factor is verified is the user marked as fully authenticated and granted access. This approach significantly improves security by protecting against stolen credentials.
MFA factors
MFA combines at least two of the following:
- Something you know β Password / PIN
- Something you have β OTP, mobile device, hardware token
- Something you are β Biometrics (fingerprint, face ID)
Most applications use Password + OTP.
Common MFA implementation approaches
(A) OTP-based MFA (most common)
- Generate time-bound OTP
- Send via: SMS Email Authenticator apps (TOTP)
Security practices:
- OTP expiry (30β120 seconds)
- Limited retry attempts
- One-time use only
Best balance of security and usability.
(B) TOTP (Time-based One-Time Password)
- Based on shared secret
- Generated by apps like Google Authenticator
- Uses time windows (RFC 6238)
Flow:
- User scans QR code during setup
- App generates OTP every 30 seconds
- Server validates OTP
No SMS dependency, more secure.
(C) Push-based MFA
- Push notification to registered device
- User approves or denies login
Used by banks and enterprise systems.
In production, Spring Security should be configured with HTTPS, stateless authentication for APIs, strong password hashing, least-privilege access, and proper CSRF handling. Tokens must be short-lived and securely refreshed, endpoints should be protected at multiple layers, and security events should be logged and monitored. Regular updates and security testing are essential to prevent misconfiguration and vulnerabilities.
Below are production-grade best practices. These focus on security, scalability, and maintainability.
1. Always use HTTPS (TLS)
- Enforce HTTPS for all endpoints
- Protects credentials, JWTs, cookies from interception
- Mandatory for OAuth2, JWT, and session cookies
Never allow authentication over HTTP in production.
2. Prefer stateless authentication for APIs
- Use JWT or OAuth2
- Disable server-side sessions
- Improves scalability and horizontal scaling
sessionCreationPolicy(SessionCreationPolicy.STATELESS)
Essential for microservices and cloud deployments.
3. Use strong password hashing
- Always hash passwords using BCrypt
- Never store or log plain-text passwords
- Avoid outdated algorithms (MD5, SHA-1)
Password hashing protects even if DB is compromised.
4. Apply the principle of least privilege
- Grant only required roles and permissions
- Avoid overusing
ROLE_ADMIN - Separate roles and permissions clearly
Reduces blast radius of compromised accounts.
5. Secure endpoints at multiple layers
- URL-level security
- Method-level security (
@PreAuthorize) - Object-level security (ACLs if needed)
Defense-in-depth approach.
6. Handle authentication & authorization errors properly
- Return correct HTTP status codes:
401β unauthenticated403β unauthorized - Do not expose internal details in error responses
Prevents information leakage.
7. Configure CSRF correctly
- Enable CSRF for session-based web apps
- Disable CSRF for stateless REST APIs
csrf(csrf -> csrf.disable())
Misconfigured CSRF is a common production issue.
8. Secure token handling (JWT best practices)
- Short-lived access tokens
- Use refresh tokens securely
- Rotate refresh tokens
- Validate issuer, audience, and expiry
Tokens should be treated like passwords.
9. Protect against common web attacks
Spring Security protects against:
- CSRF
- Session fixation
- Clickjacking
- XSS
- Brute-force attacks
Keep defaults enabled unless you fully understand them.
Role-based access control is handled by assigning roles to users and enforcing access rules based on those roles. In Spring Security, RBAC is implemented using roles as authorities and enforced at URL and method levels using configuration and annotations like @PreAuthorize.
In modern systems, roles are often embedded in JWT tokens to enable stateless authorization, and role hierarchies are used to simplify permission management.
Core idea of RBAC (start with this)
- Users β are assigned Roles
- Roles β define permissions
- Permissions β control access to resources
Example:
User β ROLE_ADMIN β manage_users, view_reports
User β ROLE_USER β view_profile
RBAC simplifies authorization by grouping permissions into roles.
GraphQL usually exposes a single endpoint (e.g. /graphql), so security cannot rely only on URL-based rules. Instead, authentication is done at the endpoint level, and authorization is enforced at resolver or method level.
GraphQL endpoints are secured by authenticating requests at the endpoint level using Spring Security, typically with JWT or OAuth2. Since GraphQL usually exposes a single endpoint, authorization is enforced at resolver or method level using annotations like @PreAuthorize. This allows fine-grained control over queries, mutations, and even specific fields, making the security model independent of REST-style URLs.