Ir al contenido

Referencia

Chuleta de referencia

Lo esencial de toda la guía en una página. Configuraciones base, tablas de decisión y el procedimiento para diagnosticar un 401 o un 403.

ApiSecurityConfig.java
@Bean
SecurityFilterChain api(HttpSecurity http) throws Exception {
http
.securityMatcher("/api/**")
.authorizeHttpRequests(auth -> auth
.requestMatchers("/error").permitAll()
.requestMatchers("/api/admin/**").hasAuthority("SCOPE_notes:admin")
.requestMatchers(HttpMethod.GET, "/api/**").hasAuthority("SCOPE_notes:read")
.anyRequest().hasAuthority("SCOPE_notes:write"))
.oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults()))
.csrf(csrf -> csrf.disable())
.sessionManagement(s -> s.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
return http.build();
}
WebSecurityConfig.java
@Bean
SecurityFilterChain web(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/", "/css/**", "/error").permitAll()
.requestMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated())
.formLogin(Customizer.withDefaults())
.logout(Customizer.withDefaults())
.headers(h -> h.contentSecurityPolicy(csp -> csp.policyDirectives("default-src 'self'")));
return http.build();
}
BffSecurityConfig.java
@Bean
SecurityFilterChain bff(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/", "/assets/**", "/error").permitAll()
.anyRequest().authenticated())
.oauth2Login(Customizer.withDefaults())
.oauth2Client(Customizer.withDefaults())
.csrf(csrf -> csrf.spa());
return http.build();
}
Si…Usa
El único cliente es una web de tu dominioSesión con cookie HttpOnly + CSRF
Hay apps móviles, servicios o tercerosTokens (JWT u opacos)
Es una SPA con login OAuth2BFF: sesión en el navegador, tokens en el servidor
La autenticación viaja en…CSRF
Una cookie (sesión, o JWT en cookie)Activado
La cabecera Authorization puesta por el clienteSe puede desactivar
ClienteGrant
Usuario en web, SPA o móvilAuthorization Code + PKCE
Servicio sin usuarioClient Credentials
Dispositivo sin tecladoDevice Authorization

Argon2id → scrypt → bcrypt (coste ≥ 10) → PBKDF2 (si se exige FIPS). Siempre con DelegatingPasswordEncoder.

PatrónCoincide
/api/notes/*Un segmento: /api/notes/5
/api/notes/**Todo lo que cuelga (solo al final del patrón)
/api/notes/{id}Captura id para reglas con access(...)
  1. Activa el log: logging.level.org.springframework.security=TRACE.
  2. ¿Qué cadena atendió la petición? Busca Securing GET /… y qué SecurityFilterChain coincidió (securityMatcher + @Order).
  3. ¿Con qué identidad? ¿AnonymousAuthenticationToken, o un usuario con qué authorities?
  4. ¿Qué la rechazó?
    • CsrfFilter → falta el token CSRF (403 en POST/PUT/DELETE).
    • Filtro de autenticación → credenciales inválidas (401).
    • AuthorizationFilter → la regla de URL (401 si anónimo, 403 si no).
    • AccessDeniedException desde un proxy → @PreAuthorize.
  5. ¿Esperabas un 400 o un 500? Revisa que /error esté en permitAll() y que ningún @ControllerAdvice se trague las excepciones de seguridad.