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.
Configuraciones base
Sección titulada «Configuraciones base»API stateless con JWT
Sección titulada «API stateless con JWT»@BeanSecurityFilterChain 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();}Aplicación web con sesión
Sección titulada «Aplicación web con sesión»@BeanSecurityFilterChain 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();}SPA con BFF
Sección titulada «SPA con BFF»@BeanSecurityFilterChain 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();}Tablas de decisión
Sección titulada «Tablas de decisión»¿Sesión o token?
Sección titulada «¿Sesión o token?»| Si… | Usa |
|---|---|
| El único cliente es una web de tu dominio | Sesión con cookie HttpOnly + CSRF |
| Hay apps móviles, servicios o terceros | Tokens (JWT u opacos) |
| Es una SPA con login OAuth2 | BFF: sesión en el navegador, tokens en el servidor |
¿CSRF activado?
Sección titulada «¿CSRF activado?»| La autenticación viaja en… | CSRF |
|---|---|
| Una cookie (sesión, o JWT en cookie) | Activado |
La cabecera Authorization puesta por el cliente | Se puede desactivar |
¿Qué grant de OAuth2?
Sección titulada «¿Qué grant de OAuth2?»| Cliente | Grant |
|---|---|
| Usuario en web, SPA o móvil | Authorization Code + PKCE |
| Servicio sin usuario | Client Credentials |
| Dispositivo sin teclado | Device Authorization |
¿Qué hash de contraseña?
Sección titulada «¿Qué hash de contraseña?»Argon2id → scrypt → bcrypt (coste ≥ 10) → PBKDF2 (si se exige FIPS). Siempre con DelegatingPasswordEncoder.
Patrones de rutas (7.x)
Sección titulada «Patrones de rutas (7.x)»| Patrón | Coincide |
|---|---|
/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(...) |
Diagnosticar un 401 o un 403
Sección titulada «Diagnosticar un 401 o un 403»- Activa el log:
logging.level.org.springframework.security=TRACE. - ¿Qué cadena atendió la petición? Busca
Securing GET /…y quéSecurityFilterChaincoincidió (securityMatcher+@Order). - ¿Con qué identidad? ¿
AnonymousAuthenticationToken, o un usuario con qué authorities? - ¿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).AccessDeniedExceptiondesde un proxy →@PreAuthorize.
- ¿Esperabas un 400 o un 500? Revisa que
/erroresté enpermitAll()y que ningún@ControllerAdvicese trague las excepciones de seguridad.