11.4 Authentication Flow and Practical Example
Authentication in Spring Security is achieved through the collaboration of several components. Here, we'll trace the full lifecycle of the most common Form Login (ID/PW) method.
1. Full Authentication Process (Mermaid)
sequenceDiagram
participant User as User(Client)
participant Filter as AuthenticationFilter
participant Manager as AuthenticationManager
participant Provider as AuthenticationProvider
participant Service as UserDetailsService
participant DB as Database
User->>Filter: 1. Login Request (username, password)
Filter->>Filter: 2. Create UsernamePasswordAuthenticationToken (unauthenticated)
Filter->>Manager: 3. Call authenticate(token)
Manager->>Provider: 4. Delegate authentication to a supporting Provider
Provider->>Service: 5. loadUserByUsername(username)
Service->>DB: 6. Query user info
DB-->>Service: Return User Entity
Service-->>Provider: 7. Return UserDetails object
Provider->>Provider: 8. Verify password (BCrypt comparison)
Provider-->>Manager: 9. Return authenticated Authentication object
Manager-->>Filter: 10. Authentication complete
Filter->>Filter: 11. Store info in SecurityContextHolder
Filter-->>User: 12. Success response/redirect
2. Roles of Key Components
- AuthenticationFilter: Intercepts the HTTP request, creates an unauthenticated
Authenticationobject, and passes it to the Manager. - AuthenticationManager: The main interface overseeing authentication. (Typically, the
ProviderManagerimplementation is used.) - AuthenticationProvider: Performs the actual business logic (e.g., ID/PW validation). Multiple providers can be registered to handle various authentication types (LDAP, DB, Social, etc.).
- UserDetailsService: Acts as the "data provider" that retrieves user information from a storage like a database.
- SecurityContextHolder: A storage area that keeps authenticated information accessible throughout the application.
3. Practical Example: Login API Implementation (JWT)
Following modern trends, here is a simple controller example that issues a JWT token after successful authentication.
LoginRequest DTO
public record LoginRequest(String email, String password) {}
AuthController
@RestController
@RequestMapping("/api/auth")
@RequiredArgsConstructor
public class AuthController {
private final AuthenticationManager authenticationManager;
private final TokenProvider tokenProvider;
@PostMapping("/login")
public ResponseEntity<String> login(@RequestBody LoginRequest request) {
// 1. Create an unauthenticated token
UsernamePasswordAuthenticationToken authenticationToken =
new UsernamePasswordAuthenticationToken(request.email(), request.password());
// 2. Start actual authentication (triggers Manager -> Provider -> Service flow)
// Throws exception if authentication fails
Authentication authentication = authenticationManager.authenticate(authenticationToken);
// 3. Store authentication info in context (optional but recommended)
SecurityContextHolder.getContext().setAuthentication(authentication);
// 4. Generate and return a JWT token based on authenticated info
String jwt = tokenProvider.createToken(authentication);
return ResponseEntity.ok(jwt);
}
}
4. Utilizing Authentication Info (@AuthenticationPrincipal)
Used when you want to directly access the logged-in user's information from a controller.
@GetMapping("/api/me")
public ResponseEntity<String> getMyInfo(@AuthenticationPrincipal UserDetails userDetails) {
return ResponseEntity.ok("Current User: " + userDetails.getUsername());
}
🎯 Key Points
- Authentication goes through the stages of Filter -> Manager -> Provider -> Service.
- The AuthenticationManager is the central role, while the Provider handles actual verification.
- Once successful, user info is held in the SecurityContextHolder, making it accessible globally.