Aller au contenu principal

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 user est 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 authentification
  • authenticated(): requiert un utilisateur authentifié
  • hasRole("X"): requiert le rôle X
  • hasAuthority("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:

  1. Header: algorithme et type
  2. Payload: claims (données)
  3. Signature: vérification d'intégrité

Format: header.payload.signature (encodé Base64)


Question 12: Quels sont les claims standard d'un JWT?

Réponse:

ClaimDescription
subSubject (utilisateur)
issIssuer (émetteur)
expExpiration
iatIssued at
nbfNot before
jtiJWT 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:

  1. Variables d'environnement
  2. Vault (HashiCorp Vault)
  3. AWS Secrets Manager
  4. 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:

  1. Blacklist: stocker les tokens révoqués (Redis)
  2. Courte expiration: tokens valides peu longtemps
  3. 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:

  1. Toujours encoder les mots de passe (BCrypt)
  2. Utiliser HTTPS en production
  3. Configurer CORS strictement
  4. Implémenter rate limiting
  5. Valider toutes les entrées
  6. Logger les événements de sécurité
  7. Utiliser des secrets forts et les externaliser
  8. Mettre à jour régulièrement les dépendances

Score​

ScoreNiveau
27-30Excellent - Sécurité maîtrisée
21-26Bien - Quelques révisions
15-20Moyen - Relisez les chapitres
Moins de 15À revoir

Retour au Module 8