🛡️ Spring Security: The Practical Guide
“Security is not a feature. It’s a mindset.”
📌 What is Spring Security?
Spring Security is a powerful framework that handles:
-
Authentication (Who are you?)
-
Authorization (What can you do?)
It provides out-of-the-box mechanisms like:
-
Username/password login
-
JWT token verification
-
Role-based access control (
@PreAuthorize) -
Session management
-
Custom filters
-
OAuth2, API key support, and more
🚀 Getting Started
1. Add Dependencies
<!-- Maven -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>🏗️ Basic Concepts
| Concept | Meaning |
|---|---|
| Authentication | Verifying identity (who the request is from) |
| Authorization | Granting access (based on roles/permissions) |
| Principal | The authenticated user or system making the call |
| GrantedAuthority | Represents a role or permission (e.g. ROLE_ADMIN) |
🔐 Define Security Configuration
@Configuration
@EnableWebSecurity
@EnableMethodSecurity(securedEnabled = true) // Enables @PreAuthorize, @Secured
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(auth -> auth
.requestMatchers("/admin/**").hasRole("ADMIN")
.requestMatchers("/public/**").permitAll()
.anyRequest().authenticated())
.httpBasic(); // or .formLogin(), or custom filters
return http.build();
}
}🔑 Common Authentication Types
✅ Form Login
- Traditional login with username/password and session cookie
http.formLogin(); // default login page✅ JWT Token (Bearer Auth)
Authorization: Bearer <token>Add a custom OncePerRequestFilter to validate and set the authentication:
SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities()));✅ API Key Authentication
X-API-KEY: your-api-keyHash and store the API key in DB, then match on request using a custom filter. Add role as SimpleGrantedAuthority.
🔓 Method-Level Security
Enable it:
@EnableMethodSecurity(prePostEnabled = true, securedEnabled = true)Use it:
@PreAuthorize("hasRole('ADMIN')")
public void adminOnlyMethod() {}👥 UserDetails & AuthenticationPrincipal
UserDetails Interface
Your User or ApiKey model can implement this:
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return List.of(new SimpleGrantedAuthority("ROLE_ADMIN"));
}Access Authenticated User
@GetMapping("/users/me")
public ResponseEntity<User> getCurrent(@AuthenticationPrincipal User user) {
return ResponseEntity.ok(user);
}🧩 Custom Filters
Custom filters can be plugged into the filter chain:
http.addFilterBefore(customFilter, UsernamePasswordAuthenticationFilter.class);Good for:
-
JWT parsing
-
API key validation
-
Request logging, etc.
📤 Exception Handling
Use Spring’s built-in mechanisms:
throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "Invalid token");Or define a global @ControllerAdvice with @ExceptionHandler.
🗃️ Summary
| Need | Solution |
|---|---|
| Session-based login | http.formLogin() |
| Token-based auth (stateless) | Custom JWT filter + SecurityContext |
| Role-based access | @PreAuthorize, getAuthorities() |
| System/API authentication | X-API-KEY with a hashed lookup |
| Secure endpoints | requestMatchers("/admin/**").hasRole("ADMIN") |
✅ Best Practices
-
Always hash and never store raw API keys or passwords.
-
Minimize use of
permitAll()— only for login or health checks. -
Use
@PreAuthorizefor service-level protection. -
Keep filters small, single-responsibility.
-
Add expiration or revocation to API keys.
🔧 Optional Enhancements
-
Add rate-limiting (e.g., Bucket4j)
-
Enable CORS properly
-
Log authentication attempts
-
Add 2FA or OAuth2
-
Use a tool like Keycloak if auth becomes complex
💬 Final Thought
Spring Security is like a bouncer at the club:
It checks who you are, what you’re allowed to do, and then lets you in — or throws you out.
Treat it with respect and your app will be safe.