Quiz: Spring Security
Testez vos connaissances sur la sécurité Spring, JWT et OAuth2.
30 questions - Cliquez sur chaque question pour révéler la réponse.
Questions 1-10: Fondamentaux Security
Question 1: Quelle dépendance ajouter pour Spring Security?
Réponse:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
Par défaut, toutes les routes sont protégées.
Question 2: Que se passe-t-il quand on ajoute Spring Security sans configuration?
Réponse:
- Toutes les routes exigent une authentification
- Un formulaire de login est affiché
- Un utilisateur
userest créé avec un mot de passe généré (affiché dans les logs) - CSRF est activé
Question 3: Comment configurer la sécurité dans Spring Boot 3?
Réponse:
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/public/**").permitAll()
.requestMatchers("/api/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
)
.httpBasic(Customizer.withDefaults());
return http.build();
}
}
Question 4: Quelle est la différence entre authenticated() et permitAll()?
Réponse:
permitAll(): accessible sans authentificationauthenticated(): requiert un utilisateur authentifiéhasRole("X"): requiert le rôle XhasAuthority("X"): requiert l'autorité X
Question 5: Comment définir des utilisateurs en mémoire?
Réponse:
@Bean
public UserDetailsService userDetailsService() {
UserDetails user = User.builder()
.username("user")
.password(passwordEncoder().encode("password"))
.roles("USER")
.build();
UserDetails admin = User.builder()
.username("admin")
.password(passwordEncoder().encode("admin"))
.roles("ADMIN")
.build();
return new InMemoryUserDetailsManager(user, admin);
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
Question 6: Comment implémenter UserDetailsService avec une base de données?
Réponse:
@Service
public class CustomUserDetailsService implements UserDetailsService {
private final UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String username) {
User user = userRepository.findByEmail(username)
.orElseThrow(() -> new UsernameNotFoundException("User not found"));
return org.springframework.security.core.userdetails.User.builder()
.username(user.getEmail())
.password(user.getPassword())
.roles(user.getRole().name())
.build();
}
}
Question 7: Pourquoi encoder les mots de passe?
Réponse:
- Sécurité: mots de passe illisibles si DB compromise
- BCrypt: génère un hash différent à chaque fois (sel intégré)
- Obligation: Spring Security refuse les mots de passe en clair
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
Question 8: Quelle est la différence entre Role et Authority?
Réponse:
- Role: préfixé par
ROLE_automatiquement - Authority: utilisé tel quel
// Ces deux sont équivalents:
.hasRole("ADMIN") // vérifie ROLE_ADMIN
.hasAuthority("ROLE_ADMIN")
// Autorités personnalisées
.hasAuthority("READ_USERS")
.hasAuthority("WRITE_USERS")
Question 9: Qu'est-ce que CSRF et quand le désactiver?
Réponse: CSRF (Cross-Site Request Forgery) protège contre les requêtes malveillantes.
Désactiver pour les API REST stateless (JWT):
http.csrf(csrf -> csrf.disable())
Garder activé pour les applications avec sessions.
Question 10: Comment récupérer l'utilisateur connecté?
Réponse:
// Dans un contrôleur
@GetMapping("/me")
public User getCurrentUser(@AuthenticationPrincipal UserDetails userDetails) {
return userService.findByEmail(userDetails.getUsername());
}
// Ou via SecurityContextHolder
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
String username = auth.getName();
Questions 11-20: JWT
Question 11: Qu'est-ce qu'un JWT?
Réponse: JSON Web Token - un token composé de 3 parties:
- Header: algorithme et type
- Payload: claims (données)
- Signature: vérification d'intégrité
Format: header.payload.signature (encodé Base64)
Question 12: Quels sont les claims standard d'un JWT?
Réponse:
| Claim | Description |
|---|---|
sub | Subject (utilisateur) |
iss | Issuer (émetteur) |
exp | Expiration |
iat | Issued at |
nbf | Not before |
jti | JWT ID (unique) |
Question 13: Comment générer un JWT?
Réponse:
@Service
public class JwtService {
@Value("${jwt.secret}")
private String secret;
public String generateToken(UserDetails user) {
return Jwts.builder()
.setSubject(user.getUsername())
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + 86400000))
.signWith(getSigningKey(), SignatureAlgorithm.HS256)
.compact();
}
private Key getSigningKey() {
return Keys.hmacShaKeyFor(Decoders.BASE64.decode(secret));
}
}
Question 14: Comment valider un JWT?
Réponse:
public boolean isTokenValid(String token, UserDetails userDetails) {
try {
Claims claims = Jwts.parserBuilder()
.setSigningKey(getSigningKey())
.build()
.parseClaimsJws(token)
.getBody();
String username = claims.getSubject();
Date expiration = claims.getExpiration();
return username.equals(userDetails.getUsername())
&& !expiration.before(new Date());
} catch (JwtException e) {
return false;
}
}
Question 15: Comment implémenter un filtre JWT?
Réponse:
@Component
public class JwtAuthFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain chain) {
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.isTokenValid(token, user)) {
UsernamePasswordAuthenticationToken auth =
new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(auth);
}
}
}
chain.doFilter(request, response);
}
}
Question 16: Comment configurer le filtre JWT?
Réponse:
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable())
.sessionManagement(session ->
session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/auth/**").permitAll()
.anyRequest().authenticated()
)
.addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class);
return http.build();
}
Question 17: Qu'est-ce qu'un Refresh Token?
Réponse: Un token de longue durée pour obtenir de nouveaux access tokens sans re-login:
- Access Token: courte durée (15min-1h)
- Refresh Token: longue durée (7-30 jours)
// Endpoint de refresh
@PostMapping("/refresh")
public TokenResponse refresh(@RequestBody RefreshRequest request) {
String refreshToken = request.getRefreshToken();
// Valider et générer nouveau access token
}
Question 18: Comment stocker le secret JWT de façon sécurisée?
Réponse:
- Variables d'environnement
- Vault (HashiCorp Vault)
- AWS Secrets Manager
- Spring Cloud Config (chiffré)
# Ne JAMAIS commiter le secret!
jwt:
secret: ${JWT_SECRET}
Question 19: Comment invalider un JWT?
Réponse: Les JWT sont stateless, donc pas révocables directement. Solutions:
- Blacklist: stocker les tokens révoqués (Redis)
- Courte expiration: tokens valides peu longtemps
- Version utilisateur: invalider à chaque changement de mot de passe
Question 20: Comment sécuriser les endpoints avec des annotations?
Réponse:
@EnableMethodSecurity
@Configuration
public class SecurityConfig { }
@RestController
public class UserController {
@PreAuthorize("hasRole('ADMIN')")
@GetMapping("/admin/users")
public List<User> getAllUsers() { }
@PreAuthorize("#id == authentication.principal.id or hasRole('ADMIN')")
@GetMapping("/users/{id}")
public User getUser(@PathVariable Long id) { }
}
Questions 21-30: OAuth2 et Sécurité Avancée
Question 21: Qu'est-ce qu'OAuth2?
Réponse: Protocole d'autorisation permettant à une application d'accéder aux ressources d'un utilisateur via un fournisseur tiers (Google, GitHub, etc.) sans partager le mot de passe.
Question 22: Quels sont les rôles dans OAuth2?
Réponse:
- Resource Owner: l'utilisateur
- Client: l'application
- Authorization Server: Google, GitHub...
- Resource Server: API protégée
Question 23: Comment configurer OAuth2 avec Google?
Réponse:
spring:
security:
oauth2:
client:
registration:
google:
client-id: ${GOOGLE_CLIENT_ID}
client-secret: ${GOOGLE_CLIENT_SECRET}
scope: openid, profile, email
http.oauth2Login(Customizer.withDefaults());
Question 24: Comment personnaliser le succès de login OAuth2?
Réponse:
http.oauth2Login(oauth -> oauth
.successHandler((request, response, authentication) -> {
OAuth2User oauthUser = (OAuth2User) authentication.getPrincipal();
userService.processOAuthUser(oauthUser);
response.sendRedirect("/dashboard");
})
);
Question 25: Qu'est-ce que CORS?
Réponse: Cross-Origin Resource Sharing - permet les requêtes depuis d'autres domaines:
@Configuration
public class CorsConfig {
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration config = new CorsConfiguration();
config.setAllowedOrigins(List.of("http://localhost:3000"));
config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE"));
config.setAllowedHeaders(List.of("*"));
config.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
return source;
}
}
Question 26: Comment ajouter des headers de sécurité?
Réponse:
http.headers(headers -> headers
.contentSecurityPolicy(csp -> csp.policyDirectives("default-src 'self'"))
.frameOptions(frame -> frame.deny())
.xssProtection(xss -> xss.enable())
);
Question 27: Comment limiter les tentatives de login?
Réponse: Implémenter un rate limiter:
@Component
public class LoginAttemptService {
private final LoadingCache<String, Integer> attemptsCache;
public void loginFailed(String ip) {
int attempts = attemptsCache.get(ip);
attemptsCache.put(ip, attempts + 1);
}
public boolean isBlocked(String ip) {
return attemptsCache.get(ip) >= MAX_ATTEMPTS;
}
}
Question 28: Comment auditer les accès sécurisés?
Réponse:
@Component
public class SecurityAuditListener {
@EventListener
public void onAuthSuccess(AuthenticationSuccessEvent event) {
log.info("Login successful: {}", event.getAuthentication().getName());
}
@EventListener
public void onAuthFailure(AuthenticationFailureBadCredentialsEvent event) {
log.warn("Login failed: {}", event.getAuthentication().getName());
}
}
Question 29: Comment tester la sécurité?
Réponse:
@WebMvcTest(UserController.class)
@Import(SecurityConfig.class)
class UserControllerSecurityTest {
@Test
@WithMockUser(roles = "ADMIN")
void adminCanAccessAdminEndpoint() {
mockMvc.perform(get("/admin/users"))
.andExpect(status().isOk());
}
@Test
@WithMockUser(roles = "USER")
void userCannotAccessAdminEndpoint() {
mockMvc.perform(get("/admin/users"))
.andExpect(status().isForbidden());
}
}
Question 30: Quelles sont les bonnes pratiques de sécurité Spring?
Réponse:
- Toujours encoder les mots de passe (BCrypt)
- Utiliser HTTPS en production
- Configurer CORS strictement
- Implémenter rate limiting
- Valider toutes les entrées
- Logger les événements de sécurité
- Utiliser des secrets forts et les externaliser
- Mettre à jour régulièrement les dépendances
Score
| Score | Niveau |
|---|---|
| 27-30 | Excellent - Sécurité maîtrisée |
| 21-26 | Bien - Quelques révisions |
| 15-20 | Moyen - Relisez les chapitres |
| Moins de 15 | À revoir |