⚙️ Step 2: Configuration Fundamentals
🧭 Series Navigation:
- ▶️ Spring Security Hub
- ▶️ Step 1: Architecture Foundation
- ✅ Step 2: Configuration Fundamentals (Current)
- ▶️ Step 3: Implementation Strategy
- ▶️ Step 4: Production Implementation
- ▶️ Step 5: Testing & Validation
🎥 YouTube Video: Coming Soon – Subscribe to JavaiOS Channel
From Default to Advanced Spring Security Setup 🔑
This guide walks through the essential steps for configuring Spring Security, starting from basic default setup and progressing to advanced, production-ready configurations. Understanding these fundamentals is crucial before implementing custom authentication providers and JWT token handling.
📋 Default Spring Security Setup
Basic Auto-Configuration
When you add Spring Security to your project, it provides sensible defaults:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
What you get automatically:
- All endpoints secured by default
- Form-based authentication
- Basic HTTP authentication
- Default user:
user
- Generated password in console logs
- CSRF protection enabled
- Session management
Default Security Filter Chain
Spring Boot automatically creates a SecurityFilterChain
equivalent to:
@Configuration
@EnableWebSecurity
public class DefaultSecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
.authorizeHttpRequests(auth -> auth
.anyRequest().authenticated()
)
.formLogin(withDefaults())
.httpBasic(withDefaults())
.build();
}
}
🚀 Transitioning to Custom Configuration
Step 1: Create Custom Security Configuration
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(auth -> auth
.requestMatchers("/public/**").permitAll()
.requestMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
)
.sessionManagement(session -> session
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
)
.build();
}
}
Step 2: Custom UserDetailsService
@Service
public class CustomUserDetailsService implements UserDetailsService {
@Autowired
private UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
User user = userRepository.findByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException(
"User not found: " + username));
return org.springframework.security.core.userdetails.User
.withUsername(user.getUsername())
.password(user.getPassword())
.authorities(user.getRoles())
.accountExpired(false)
.accountLocked(false)
.credentialsExpired(false)
.disabled(false)
.build();
}
}
Step 3: Password Encoder Configuration
@Configuration
public class PasswordConfig {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(12);
}
}
⚡ Advanced Configuration with JWT
Complete Security Configuration
@Configuration
@EnableWebSecurity
public class AdvancedSecurityConfig {
@Autowired
private CustomUserDetailsService userDetailsService;
@Autowired
private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
@Autowired
private JwtRequestFilter jwtRequestFilter;
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(auth -> auth
.requestMatchers(
"/auth/login",
"/auth/register",
"/public/**",
"/v3/api-docs/**",
"/swagger-ui/**"
).permitAll()
.requestMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
)
.exceptionHandling(ex -> ex
.authenticationEntryPoint(jwtAuthenticationEntryPoint)
)
.sessionManagement(session -> session
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
)
.authenticationProvider(authenticationProvider())
.addFilterBefore(jwtRequestFilter,
UsernamePasswordAuthenticationFilter.class)
.build();
}
@Bean
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authProvider =
new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService);
authProvider.setPasswordEncoder(passwordEncoder());
return authProvider;
}
@Bean
public AuthenticationManager authenticationManager(
AuthenticationConfiguration config) throws Exception {
return config.getAuthenticationManager();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(12);
}
}
JWT Authentication Filter
@Component
public class JwtRequestFilter extends OncePerRequestFilter {
@Autowired
private CustomUserDetailsService userDetailsService;
@Autowired
private JwtUtil jwtUtil;
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
final String requestTokenHeader = request.getHeader("Authorization");
String username = null;
String jwtToken = null;
// JWT Token is in the form "Bearer token"
if (requestTokenHeader != null &&
requestTokenHeader.startsWith("Bearer ")) {
jwtToken = requestTokenHeader.substring(7);
try {
username = jwtUtil.getUsernameFromToken(jwtToken);
} catch (IllegalArgumentException e) {
logger.error("Unable to get JWT Token");
} catch (ExpiredJwtException e) {
logger.error("JWT Token has expired");
}
}
// Validate token
if (username != null &&
SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails =
this.userDetailsService.loadUserByUsername(username);
if (jwtUtil.validateToken(jwtToken, userDetails)) {
UsernamePasswordAuthenticationToken authToken =
new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
authToken.setDetails(
new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authToken);
}
}
chain.doFilter(request, response);
}
}
🔧 Configuration Best Practices
1. Security Filter Order
Understanding filter execution order is crucial:
// Correct filter ordering
.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class)
.addFilterAfter(customAuthFilter, BasicAuthenticationFilter.class)
2. Exception Handling
@Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request,
HttpServletResponse response,
AuthenticationException authException) throws IOException {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED,
"Unauthorized: " + authException.getMessage());
}
}
3. CORS Configuration
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOriginPatterns(Arrays.asList("*"));
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE"));
configuration.setAllowedHeaders(Arrays.asList("*"));
configuration.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source =
new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
📊 Configuration Comparison
Feature | Default Configuration | Advanced Configuration |
---|---|---|
Authentication | Form login + Basic Auth | JWT + Custom Provider |
Session Management | Session-based | Stateless (JWT) |
User Store | In-memory | Database via UserDetailsService |
Password Encoding | Default delegating encoder | BCrypt with custom strength |
CSRF Protection | Enabled | Disabled (stateless) |
Exception Handling | Default error pages | Custom JSON responses |
🔍 Common Configuration Issues
Issue 1: Filter Order Problems
Problem: JWT filter not executing before authentication
// ❌ Wrong
.addFilterAfter(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class)
// ✅ Correct
.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class)
Issue 2: Circular Bean Dependencies
Problem: AuthenticationManager circular dependency
// ❌ Can cause circular dependency
@Autowired
private AuthenticationManager authenticationManager;
// ✅ Better approach
@Bean
public AuthenticationManager authenticationManager(
AuthenticationConfiguration config) throws Exception {
return config.getAuthenticationManager();
}
Issue 3: CORS Issues with JWT
Problem: Preflight requests failing
// ✅ Add CORS configuration
.cors(cors -> cors.configurationSource(corsConfigurationSource()))
✅ Configuration Checklist
Security Configuration:
- ✅ CSRF disabled for stateless JWT
- ✅ Session policy set to STATELESS
- ✅ Public endpoints configured with permitAll()
- ✅ Protected endpoints require authentication
- ✅ Custom authentication provider registered
JWT Configuration:
- ✅ JWT filter added before username/password filter
- ✅ Custom authentication entry point configured
- ✅ Token validation logic implemented
- ✅ Security context set properly
Additional Security:
- ✅ CORS configured for frontend integration
- ✅ Password encoder with appropriate strength
- ✅ Exception handling for authentication failures
- ✅ Proper filter ordering maintained
🚀 Continue Your Security Journey
🧭 Next Steps in Spring Security:
- ▶️ Spring Security Hub
- ▶️ Step 1: Architecture Foundation
- ✅ Step 2: Configuration Fundamentals (Current)
- ▶️ Step 3: Implementation Strategy
- ▶️ Step 4: Production Implementation
- ▶️ Step 5: Testing & Validation
Part of the AdventureTube technical blog series supporting the JavaiOS YouTube channel.