Hook: Interviewers love this because one tiny mix-up can turn into a security bug: who you are is not the same as what you may do.
Question: What is the difference between authentication and authorization in Spring Boot?
Answer: Authentication means verifying identity: the app checks that you are really the user you claim to be, usually by username/password, SSO, or a token. Authorization means checking permission: once the app knows who you are, it decides whether you can access a URL, method, or data item. In Spring Security, authentication fills the SecurityContext with the current user, and authorization uses that user’s roles or authorities to allow or deny access.
Interview-Ready Answer: In Spring Boot, authentication is the step where I prove identity, like logging in with credentials or a token. Authorization is the next step where Spring Security checks my roles, authorities, or ownership rules to decide what I can actually access. A simple way I remember it is: authentication answers “Who are you?” and authorization answers “What are you allowed to do?”. A nice detail is that Spring Security usually returns 401 for missing/invalid login and 403 when I’m logged in but not allowed.
Think of Spring Security as a gate system. Authentication is the gatekeeper checking your ID. Authorization is the person inside deciding which rooms you may enter. You can be a real person and still not be allowed into every room.
AuthenticationManager and one or more AuthenticationProviders. For example, a username/password login may be checked against a database, and a password hash such as BCrypt is verified.SecurityContext. The SecurityContext is the per-request place where Spring keeps Authentication, which includes the principal (the user), credentials, and granted authorities.authenticated(), hasRole('ADMIN'), or method rules like @PreAuthorize.401 Unauthorized. If the user is authenticated but not allowed, Spring returns 403 Forbidden.| Aspect | Authentication | Authorization |
|---|---|---|
| Main question | Who are you? | What can you do? |
| Input | Password, token, SSO, cert | Roles, authorities, claims, ownership |
| Spring result | Authentication created | Access allowed or denied |
| Typical failure | 401 | 403 |
| Cost | Often expensive | Usually cheap |
SecurityFilterChain and authorizeHttpRequests; WebSecurityConfigurerAdapter is removed.hasRole('ADMIN') vs hasAuthority('ADMIN'): hasRole automatically expects ROLE_ADMIN. This prefix trips up many candidates.Memory model: authentication is the badge check, authorization is the door list. One says “yes, it’s really you”; the other says “yes, you may enter this room.”
Imagine a checkout service in an e-commerce app. Customers can view their own orders, and support agents can view more, but only admins can refund payments. The team correctly adds login, so every request has an authenticated user. But they forget the authorization rule on /orders/{id} and only check that the user is logged in.
What happens? A customer guesses another order ID and sees someone else’s address, items, and payment status. In logs, nothing looks “broken” because the endpoint returns 200 OK. The real symptom is user complaints: “I can see orders that are not mine.” A second common incident is a bad role mapping, where the app stores ADMIN but the code uses hasRole('ADMIN'), so everyone gets 403 after deploy.
The lesson: authentication stops strangers from pretending to be users; authorization stops valid users from overreaching.
package com.example.authdemo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@SpringBootApplication
public class AuthVsAuthzApplication {
public static void main(String[] args) {
SpringApplication.run(AuthVsAuthzApplication.class, args);
}
}
@Configuration
class SecurityConfig {
@Bean
PasswordEncoder passwordEncoder() {
// BCrypt is intentionally slow enough to make brute force harder.
return new BCryptPasswordEncoder();
}
@Bean
UserDetailsService userDetailsService(PasswordEncoder encoder) {
// In-memory users keep the demo runnable with no database setup.
// Note the ROLE_ prefix is added automatically by .roles(...).
return new InMemoryUserDetailsManager(
User.withUsername("user")
.password(encoder.encode("password"))
.roles("USER")
.build(),
User.withUsername("admin")
.password(encoder.encode("password"))
.roles("ADMIN")
.build()
);
}
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http,
AuthenticationEntryPoint authEntryPoint,
AccessDeniedHandler deniedHandler) throws Exception {
http
.csrf(csrf -> csrf.disable()) // Fine for this GET-only demo; real apps must decide carefully.
.authorizeHttpRequests(auth -> auth
.requestMatchers("/public").permitAll()
.requestMatchers("/me").authenticated()
// hasRole("ADMIN") expects authority ROLE_ADMIN.
.requestMatchers("/admin").hasRole("ADMIN")
.anyRequest().denyAll()
)
.httpBasic(Customizer.withDefaults()) // Easy to test with curl and keeps the demo focused.
.exceptionHandling(ex -> ex
.authenticationEntryPoint(authEntryPoint) // 401 when the user is missing or invalid.
.accessDeniedHandler(deniedHandler) // 403 when logged in but not allowed.
);
return http.build();
}
@Bean
AuthenticationEntryPoint authEntryPoint() {
return (HttpServletRequest request, HttpServletResponse response, org.springframework.security.core.AuthenticationException ex) -> {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
response.getWriter().write("{\"error\":\"unauthenticated\",\"message\":\"Login required\"}");
};
}
@Bean
AccessDeniedHandler deniedHandler() {
return (HttpServletRequest request, HttpServletResponse response,
org.springframework.security.access.AccessDeniedException ex) -> {
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
response.getWriter().write("{\"error\":\"forbidden\",\"message\":\"You are logged in, but not allowed here\"}");
};
}
}
@RestController
class DemoController {
@GetMapping("/public")
Map<String, Object> publicEndpoint() {
return Map.of("message", "Anyone can read this.");
}
@GetMapping("/me")
Map<String, Object> me(Authentication authentication) {
// Authentication proves identity: Spring injects the current logged-in user here.
return Map.of(
"username", authentication.getName(),
"authorities", toAuthorityStrings(authentication)
);
}
@GetMapping("/admin")
Map<String, Object> admin(Authentication authentication) {
// If a USER calls this, Spring returns 403 even though the login was valid.
return Map.of(
"message", "Admin-only data",
"viewer", authentication.getName()
);
}
private List<String> toAuthorityStrings(Authentication authentication) {
return authentication.getAuthorities().stream()
.map(GrantedAuthority::getAuthority)
.collect(Collectors.toList());
}
}
Follow-up & Tricky Questions:
401 and 403? 401 means the request is not authenticated or the credentials are invalid. 403 means the user is authenticated, but the action is not permitted.ROLE_.... Roles are just a common kind of authority.SecurityContext, usually backed by the SecurityContextHolder for the current thread/request.@PreAuthorize protects service methods, which is useful when the same service is called from multiple endpoints.hasRole('ADMIN') check for ADMIN or ROLE_ADMIN? It checks for ROLE_ADMIN. That prefix rule is one of the most common Spring Security interview traps.Common Mistakes:
401 for missing/invalid identity and 403 for valid identity with insufficient permission.ROLE_ prefix — Correction: hasRole('ADMIN') expects ROLE_ADMIN; use hasAuthority if you want the exact string.Memory Hook: “ID first, door list second.” Authentication checks the ID card; authorization checks the room list.
Cheat Sheet:
SecurityContext.401 = not authenticated; 403 = authenticated but blocked.hasRole('X') expects ROLE_X.Practice Tasks:
/profile endpoint that returns the current username and authorities./admin to use hasAuthority('ROLE_ADMIN') and verify the behavior is the same as hasRole('ADMIN')./admin with no credentials, with user/password, and with admin/password; observe 401, 403, and success.