Skip to content

Latest commit

 

History

History

37-Spring-Security

// concepto

Spring Security protege aplicaciones mediante una cadena de filtros Servlet (SecurityFilterChain) que intercepta cada request HTTP antes de que llegue al controller.

Diferencia fundamental:

Concepto Pregunta Ejemplo
Autenticación ¿Quién eres? Login con usuario/contraseña
Autorización ¿Qué puedes hacer? Solo ADMIN puede acceder a /admin

Flujo de autenticación (form login / JWT):

HTTP Request
    ↓
DelegatingFilterProxy          ← puente Spring MVC ↔ Servlet
    ↓
FilterChainProxy               ← gestiona la SecurityFilterChain
    ↓
UsernamePasswordAuthenticationFilter  (o JwtAuthFilter custom)
    ↓
AuthenticationManager          ← orquesta la autenticación
    ↓
UserDetailsService.loadUserByUsername(username)
    ↓
UserDetails (username, password, roles)
    ↓
PasswordEncoder.matches()      ← valida el hash
    ↓
SecurityContextHolder          ← almacena Authentication para el request
    ↓
Controller

Configuración básica con SecurityFilterChain:

// @Configuration
// @EnableWebSecurity
public class SecurityConfig {

    // @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .csrf(csrf -> csrf.disable())            // desactivar para APIs REST
            .sessionManagement(session -> session
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS))
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/api/auth/**").permitAll()
                .requestMatchers("/api/admin/**").hasRole("ADMIN")
                .anyRequest().authenticated()
            );
        return http.build();
    }
}
// key features

UserDetailsService + UserDetails:

// @Service
public class UserDetailsServiceImpl implements UserDetailsService {

    // @Autowired
    private UsuarioRepository repo;

    // @Override
    public UserDetails loadUserByUsername(String email) {
        Usuario u = repo.findByEmail(email)
            .orElseThrow(() -> new UsernameNotFoundException(email));

        return User.builder()
            .username(u.getEmail())
            .password(u.getPasswordHash())   // ya hasheado en BD
            .roles(u.getRol())
            .build();
    }
}

PasswordEncoder — BCrypt:

// @Bean
public PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder(12);   // cost factor: 2^12 iteraciones
}

// Al registrar usuario:
String hash = encoder.encode("miContraseña");   // genera salt + hash
// Al verificar login:
boolean valido = encoder.matches("miContraseña", hash);  // siempre constante-time

BCrypt incluye un salt aleatorio en cada hash y el cost factor (factor de trabajo) controla las iteraciones: a mayor valor, más lento el brute-force. El valor por defecto es 10; en producción se usa 12.

JWT — estructura y flujo:

Header.Payload.Signature

eyJhbGciOiJIUzI1NiJ9  .  eyJzdWIiOiJhbmFAZW1haWwuY29tIiwicm9sZSI6IkFETUlOIn0  .  xY7...

Header: { "alg": "HS256" }
Payload: { "sub": "ana@email.com", "role": "ADMIN", "exp": 1716000000 }
Signature: HMAC-SHA256(base64(header) + "." + base64(payload), secretKey)

Flujo JWT:

POST /auth/login  →  valida credenciales  →  devuelve JWT
GET  /api/datos   →  Authorization: Bearer <token>
                  →  JwtAuthFilter valida firma + expiración
                  →  establece SecurityContext  →  Controller

Autorización por método:

// @EnableMethodSecurity  ← habilitar en config

// @PreAuthorize("hasRole('ADMIN')")
public void eliminarUsuario(Long id) { ... }

// @Secured("ROLE_ADMIN")
public List<Usuario> listarTodos() { ... }

// @PreAuthorize("hasRole('USER') and #id == authentication.principal.id")
public Usuario verPerfil(Long id) { ... }

Sesiones stateful vs stateless:

Stateful (sesiones) Stateless (JWT)
Estado Servidor almacena sesión Todo en el token
Escalabilidad Sticky sessions o session store compartido Horizontal sin coordinación
Revocación Inmediata (borra sesión) Difícil (hasta que expira)
Uso típico Apps MVC tradicionales APIs REST, microservicios

OAuth2 Resource Server (configuración mínima):

// application.yml
// spring:
//   security:
//     oauth2:
//       resourceserver:
//         jwt:
//           issuer-uri: https://auth.ejemplo.com

http.oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults()));
// ventajas
  • Integración transparente con Spring MVC: Spring Security se engancha al ciclo de vida de Servlet sin modificar los controllers.
  • Extensible: cada parte del flujo (UserDetailsService, PasswordEncoder, AuthenticationProvider) es un bean reemplazable.
  • Soporte OAuth2/OIDC nativo: Resource Server y Authorization Server disponibles out-of-the-box.
  • Defensa en profundidad: CSRF, headers de seguridad (X-Content-Type-Options, X-Frame-Options), clickjacking protection — todo activado por defecto.

Ver ExpJwtSimulation.java para el flujo JWT completo con Java puro y ExpSecurityChain.java para la simulación de la cadena de filtros.