Spring Security Fundamentals
Spring Security is the standard for securing Java applications. Add one dependency and everything is locked down — then you configure what to open.
Setup
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
What happens immediately:
- All endpoints require authentication
- A login page appears at
/login - A default user
useris created with a random password (printed in logs) - CSRF protection is enabled
- Session-based authentication is active
How Spring Security Works
HTTP Request
│
▼
┌────────────────────────────────────────┐
│ Security Filter Chain │
│ │
│ 1. CorsFilter ← CORS headers │
│ 2. CsrfFilter ← CSRF token │
│ 3. AuthenticationFilter ← Who are you? │
│ 4. AuthorizationFilter ← Can you? │
│ │
└────────────────────────────────────────┘
│
▼
Controller (if allowed)
Authentication vs Authorization
| Concept | Question | Example |
|---|---|---|
| Authentication | Who are you? | Login with username/password |
| Authorization | What can you do? | Admin can delete, user can only read |
SecurityFilterChain Configuration
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable()) // disable for REST APIs
.sessionManagement(session ->
session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/auth/**").permitAll() // public
.requestMatchers("/swagger-ui/**", "/v3/api-docs/**").permitAll()
.requestMatchers(HttpMethod.GET, "/api/books/**").permitAll()
.requestMatchers("/api/admin/**").hasRole("ADMIN")
.anyRequest().authenticated() // everything else
);
return http.build();
}
}
Authorization Rules
| Method | Meaning |
|---|---|
permitAll() |
No auth required |
authenticated() |
Must be logged in |
hasRole("ADMIN") |
Must have ADMIN role |
hasAnyRole("ADMIN", "MANAGER") |
Any of these roles |
hasAuthority("BOOK_WRITE") |
Specific permission |
CORS Configuration
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.cors(cors -> cors.configurationSource(corsConfigurationSource()))
// ... other config
;
return http.build();
}
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration config = new CorsConfiguration();
config.setAllowedOrigins(List.of("http://localhost:3000", "https://bookstore.com"));
config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE"));
config.setAllowedHeaders(List.of("*"));
config.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/api/**", config);
return source;
}
CSRF
| Scenario | CSRF |
|---|---|
| REST API (stateless, JWT) | Disable |
| Server-rendered pages (sessions) | Keep enabled |
// Disable for stateless REST APIs
.csrf(csrf -> csrf.disable())
Password Encoder
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
Never store plain-text passwords. BCrypt hashes are one-way and include a salt.
Key Takeaways
- Adding
spring-boot-starter-securitylocks down everything by default. SecurityFilterChaindefines which URLs are public vs protected.- CSRF: disable for REST APIs, keep for server-rendered pages.
- CORS must be configured for frontend-backend communication.
- Always use
BCryptPasswordEncoder— never store plain passwords.
What’s Next?
Next: Basic Authentication — in-memory users, UserDetailsService, and password encoding.