Capítulo 1: Introducción a Spring Security
Introducción
Antes de configurar cualquier cosa, es esencial comprender cómo funciona Spring Security. Este capítulo sienta las bases que le permitirán configurar la seguridad correctamente.
🎯 Objetivos del Capítulo
Al final de este capítulo, usted sabrá:
- ✅ Por qué la seguridad es esencial
- ✅ La diferencia entre Autenticación y Autorización
- ✅ La arquitectura de Spring Security
- ✅ Cómo funciona la cadena de filtros
- ✅ Los conceptos clave: Principal, Credenciales, Autoridades
1. ¿Por qué asegurar una aplicación?
Las Amenazas
Ejemplos Reales
| Año | Empresa | Impacto |
|---|---|---|
| 2023 | MOVEit | 60M+ usuarios afectados |
| 2021 | 533M cuentas expuestas | |
| 2019 | Capital One | 100M+ datos robados |
| 2017 | Equifax | 147M datos personales |
Los 3 Pilares de la Seguridad (CIA)
┌─────────────────────────────────────────────────┐
│ │
│ C - Confidencialidad │
│ Solo las personas autorizadas │
│ pueden ver los datos │
│ │
│ I - Integridad │
│ Los datos no pueden ser │
│ modificados sin autorización │
│ │
│ A - Disponibilidad │
│ El sistema permanece accesible │
│ para los usuarios legítimos │
│ │
└─────────────────────────────────────────────────┘
2. Autenticación vs Autorización
La Diferencia Fundamental
| Concepto | Pregunta | Ejemplo |
|---|---|---|
| Autenticación | "¿Quién es usted?" | Login con email/contraseña |
| Autorización | "¿Tiene permiso?" | Acceso a /admin (reservado a admins) |
Analogía: El Club VIP
En Spring Security
// Authentication: Qui est connecté?
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
String username = auth.getName(); // "jean@example.com"
// Authorization: Quels sont ses droits?
Collection<? extends GrantedAuthority> authorities = auth.getAuthorities();
// [ROLE_USER] - Pas ROLE_ADMIN!
3. Arquitectura de Spring Security
Vista General
Los Componentes Clave
1. SecurityFilterChain
La cadena de filtros que intercepta TODAS las solicitudes:
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/public/**").permitAll()
.requestMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
)
.formLogin(Customizer.withDefaults())
.build();
}
}
2. AuthenticationManager
Coordina la autenticación:
3. UserDetailsService
Interfaz para cargar los usuarios:
public interface UserDetailsService {
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
}
4. PasswordEncoder
Codifica las contraseñas (¡NUNCA en texto plano!):
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
// Utilización
String encoded = passwordEncoder.encode("monMotDePasse");
// $2a$10$N9qo8uLOickgx2ZMRZoMye...
boolean matches = passwordEncoder.matches("monMotDePasse", encoded);
// true
4. La Cadena de Filtros en Detalle
Orden de los Filtros (Simplificado)
1. CorsFilter → Gestión CORS
2. CsrfFilter → Protección CSRF
3. LogoutFilter → Gestión de cierre de sesión
4. UsernamePasswordAuthFilter → Login por formulario
5. BasicAuthenticationFilter → HTTP Basic Auth
6. BearerTokenAuthFilter → JWT/OAuth2
7. AnonymousAuthenticationFilter → Usuario anónimo
8. ExceptionTranslationFilter → Gestión de errores
9. FilterSecurityInterceptor → Decisión de acceso final
Visualización del Flujo
5. Conceptos Clave
Principal
La identidad del usuario conectado:
@GetMapping("/profile")
public String profile(Principal principal) {
String username = principal.getName();
return "Bienvenido " + username;
}
Authentication Object
Contiene toda la información de autenticación:
public interface Authentication {
Collection<? extends GrantedAuthority> getAuthorities(); // Roles/Permisos
Object getCredentials(); // Contraseña (a menudo borrada después de auth)
Object getDetails(); // Detalles adicionales (IP, etc.)
Object getPrincipal(); // El usuario (UserDetails)
boolean isAuthenticated();
}
GrantedAuthority
Representa un rol o un permiso:
// Roles (prefijo ROLE_)
new SimpleGrantedAuthority("ROLE_ADMIN");
new SimpleGrantedAuthority("ROLE_USER");
// Permisos
new SimpleGrantedAuthority("READ_USERS");
new SimpleGrantedAuthority("WRITE_USERS");
new SimpleGrantedAuthority("DELETE_USERS");
SecurityContext
Almacena la Authentication del usuario actual:
// Recuperar el usuario conectado
SecurityContext context = SecurityContextHolder.getContext();
Authentication auth = context.getAuthentication();
if (auth != null && auth.isAuthenticated()) {
String username = auth.getName();
// ...
}
UserDetails
Interfaz que representa un usuario:
public interface UserDetails {
Collection<? extends GrantedAuthority> getAuthorities();
String getPassword();
String getUsername();
boolean isAccountNonExpired();
boolean isAccountNonLocked();
boolean isCredentialsNonExpired();
boolean isEnabled();
}
6. Tipos de Autenticación
1. Form Login (Sesión)
Cliente → POST /login (user, pass)
Servidor → Set-Cookie: JSESSIONID=xyz
Cliente → GET /api/data (Cookie: JSESSIONID=xyz)
Servidor → Verifica sesión → 200 OK
Ventajas: Simple, gestionado por el navegador
Desventajas: No apto para API REST stateless
2. HTTP Basic
Cliente → GET /api/data
Servidor → 401 + WWW-Authenticate: Basic
Cliente → GET /api/data (Authorization: Basic base64(user:pass))
Servidor → 200 OK
Ventajas: Simple
Desventajas: Credenciales enviadas en cada solicitud
3. JWT (Token)
Cliente → POST /login (user, pass)
Servidor → {"token": "eyJhbGciOiJIUzI1..."}
Cliente → GET /api/data (Authorization: Bearer eyJhbGciOiJIUzI1...)
Servidor → Verifica token → 200 OK
Ventajas: Stateless, perfecto para API REST
Desventajas: Gestión de la expiración
4. OAuth2
Cliente → Redirección a Google
Google → Login usuario
Google → Redirección con código
Servidor → Intercambia código por token
Servidor → Recupera perfil de usuario
Ventajas: No se gestiona contraseña, UX moderna
Desventajas: Dependencia externa
7. Configuración Moderna (Spring Security 6+)
Método Antiguo (Obsoleto)
// ❌ NO USAR MÁS
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated();
}
}
Nuevo Método (Spring Security 6+)
// ✅ UTILICE ESTE ENFOQUE
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
)
.formLogin(Customizer.withDefaults())
.build();
}
@Bean
public UserDetailsService userDetailsService() {
UserDetails user = User.withDefaultPasswordEncoder()
.username("user")
.password("password")
.roles("USER")
.build();
return new InMemoryUserDetailsManager(user);
}
}
8. Primera Configuración
Dependencia Maven
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
Comportamiento por Defecto
Sin ninguna configuración, Spring Security:
- Protege TODOS los endpoints
- Genera una contraseña aleatoria (mostrada en los logs)
- Activa el form login en
/login - Activa el logout en
/logout
Using generated security password: 8e3d7a2c-1234-5678-abcd-ef0123456789
Configuración Mínima
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/", "/home", "/public/**").permitAll()
.anyRequest().authenticated()
)
.formLogin(form -> form
.loginPage("/login")
.permitAll()
)
.logout(logout -> logout
.permitAll()
)
.build();
}
}
9. Resumen
Conceptos Clave
| Concepto | Descripción |
|---|---|
| Autenticación | Verificar la identidad (¿quién?) |
| Autorización | Verificar los permisos (¿qué?) |
| SecurityFilterChain | Cadena de filtros de seguridad |
| AuthenticationManager | Orquesta la autenticación |
| UserDetailsService | Carga los usuarios |
| PasswordEncoder | Codifica las contraseñas |
| SecurityContext | Almacena el usuario actual |
Arquitectura Simplificada
Solicitud HTTP
↓
SecurityFilterChain (filtros)
↓
AuthenticationManager
↓
UserDetailsService + PasswordEncoder
↓
SecurityContext (almacenamiento)
↓
Aplicación (Controller)
Puntos Importantes
✅ Spring Security 6+ utiliza @Bean, no extiende WebSecurityConfigurerAdapter
✅ Siempre codifique las contraseñas (BCrypt)
✅ Comprenda Autenticación vs Autorización
✅ La seguridad se configura vía SecurityFilterChain
🎯 Próximo Paso
Ahora que comprende la arquitectura, pasemos a la práctica con la autenticación básica.
➡️ Capítulo 2: Autenticación Básica