On this page
Authentication
Spring Security authentication verifies who a user is. It integrates with servlet filters, supports multiple authentication mechanisms, and stores the authenticated principal in the SecurityContext.
Core Components
| Component | Role |
|---|---|
AuthenticationManager |
Processes authentication requests |
UserDetailsService |
Loads user-specific data |
PasswordEncoder |
Hashes and verifies passwords |
AuthenticationProvider |
Implements authentication logic |
SecurityContextHolder |
Stores current authentication |
Basic Setup
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/public/**").permitAll()
.anyRequest().authenticated()
)
.formLogin(form -> form
.loginPage("/login")
.defaultSuccessUrl("/dashboard")
.permitAll()
)
.logout(logout -> logout.logoutSuccessUrl("/login?logout"));
return http.build();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
UserDetailsService
@Service
public class CustomUserDetailsService implements UserDetailsService {
private final UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByEmail(username)
.orElseThrow(() -> new UsernameNotFoundException("User not found: " + username));
return org.springframework.security.core.userdetails.User.builder()
.username(user.getEmail())
.password(user.getPasswordHash())
.roles(user.getRole())
.disabled(!user.isActive())
.build();
}
}
DaoAuthenticationProvider Flow
Login request → UsernamePasswordAuthenticationToken
→ AuthenticationManager
→ DaoAuthenticationProvider
→ UserDetailsService.loadUserByUsername()
→ PasswordEncoder.matches(raw, encoded)
→ Authenticated token stored in SecurityContext
HTTP Basic Authentication
http.httpBasic(Customizer.withDefaults());
Client sends: Authorization: Basic base64(username:password)
JWT Authentication Filter
@Component
public class JwtAuthFilter extends OncePerRequestFilter {
private final JwtService jwtService;
private final UserDetailsService userDetailsService;
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain chain) throws ServletException, IOException {
String header = request.getHeader("Authorization");
if (header != null && header.startsWith("Bearer ")) {
String token = header.substring(7);
String username = jwtService.extractUsername(token);
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails user = userDetailsService.loadUserByUsername(username);
if (jwtService.isValid(token, user)) {
var auth = new UsernamePasswordAuthenticationToken(
user, null, user.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(auth);
}
}
}
chain.doFilter(request, response);
}
}
Password Encoding
Never store plaintext passwords:
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(12); // strength factor
}
// Registration
String hash = passwordEncoder.encode(rawPassword);
user.setPasswordHash(hash);
// Login verification (handled by Spring Security)
passwordEncoder.matches(rawPassword, storedHash);
| Encoder | Use case |
|---|---|
BCryptPasswordEncoder |
Default choice |
Argon2PasswordEncoder |
Modern, memory-hard |
PBKDF2PasswordEncoder |
FIPS compliance |
Best Practices
- Always use a strong
PasswordEncoder— never MD5 or SHA-1 - Implement
UserDetailsServiceagainst your user store - Use stateless JWT for REST APIs; session-based for web apps
- Protect login endpoints against brute force (rate limiting, CAPTCHA)
- Never log passwords or tokens