🔐 Login con Spring Security + MVC + MySQL + Thymeleaf + Bootstrap

 

🧩 Guía COMPLETA y EXPLICADA paso a paso (nivel principiante → intermedio)

En este tutorial no solo vas a copiar código: vas a entender cómo funciona cada pieza de un sistema de autenticación real con Spring Boot.


🧠 1. ¿Qué estamos construyendo?

Vamos a crear un sistema que:

  • Permite iniciar sesión con usuario y contraseña

  • Valida credenciales contra MySQL

  • Protege rutas según roles (ADMIN / USER)

  • Usa vistas con Thymeleaf

  • Tiene diseño con Bootstrap

👉 Todo esto usando el patrón MVC (Modelo - Vista - Controlador).


🏗️ 2. Crear el proyecto

Puedes usar Spring Initializr o NetBeans.

Selecciona:

  • Spring Web → para controladores y MVC

  • Spring Security → autenticación

  • Spring Data JPA → acceso a base de datos

  • Thymeleaf → vistas HTML dinámicas

  • MySQL Driver → conexión a BD


📦 3. Dependencias (¿por qué son importantes?)

<dependencies>

    <!-- Permite crear controladores y endpoints web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!-- Motor de plantillas HTML -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>

    <!-- Seguridad: login, roles, filtros -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>

    <!-- ORM: convierte tablas en objetos Java -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>

    <!-- Conexión a MySQL -->
    <dependency>
        <groupId>com.mysql</groupId>
        <artifactId>mysql-connector-j</artifactId>
    </dependency>

</dependencies>

💡 Idea clave:
Spring Boot trabaja por módulos. Cada dependencia activa funcionalidades automáticamente (auto-configuración).


🗄️ 4. Base de datos (modelo real)

CREATE DATABASE login_db;
USE login_db;

CREATE TABLE roles (
    id INT AUTO_INCREMENT PRIMARY KEY,
    nombre VARCHAR(50)
);

CREATE TABLE usuarios (
    id INT AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(50) UNIQUE,
    password VARCHAR(255),
    enabled BOOLEAN
);

CREATE TABLE usuario_roles (
    usuario_id INT,
    rol_id INT,
    FOREIGN KEY (usuario_id) REFERENCES usuarios(id),
    FOREIGN KEY (rol_id) REFERENCES roles(id)
);

🔍 Explicación

  • usuarios → almacena credenciales

  • roles → define permisos (ADMIN, USER)

  • usuario_roles → relación muchos a muchos

💡 Esto permite que:

  • Un usuario tenga varios roles

  • Un rol pertenezca a varios usuarios


⚙️ 5. application.properties

spring.datasource.url=jdbc:mysql://localhost:3306/login_db
spring.datasource.username=root
spring.datasource.password=

spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true

spring.thymeleaf.cache=false

🔍 Explicación

  • ddl-auto=update → crea/actualiza tablas automáticamente

  • show-sql=true → muestra consultas en consola

  • cache=false → útil en desarrollo (recarga HTML sin reiniciar)


📁 6. Estructura del proyecto (MVC)

com.tuapp
│
├── controller  → recibe peticiones HTTP
├── service     → lógica de negocio
├── repository  → acceso a datos
├── model       → entidades (tablas)
├── security    → configuración de seguridad
└── config      → configuración general

💡 Clave: Separar responsabilidades evita código desordenado.


🧩 7. Entidades (JPA)

👤 Usuario

@Entity
@Table(name = "usuarios")
public class Usuario {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String username;
    private String password;
    private boolean enabled;

    @ManyToMany(fetch = FetchType.EAGER)
    @JoinTable(
        name = "usuario_roles",
        joinColumns = @JoinColumn(name = "usuario_id"),
        inverseJoinColumns = @JoinColumn(name = "rol_id")
    )
    private List<Rol> roles;
}

🔍 Explicación

  • @Entity → convierte la clase en tabla

  • @ManyToMany → relación con roles

  • EAGER → carga roles automáticamente (importante para seguridad)


🔑 Rol

@Entity
@Table(name = "roles")
public class Rol {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String nombre;
}

🗃️ 8. Repository

public interface UsuarioRepository extends JpaRepository<Usuario, Long> {
    Optional<Usuario> findByUsername(String username);
}

🔍 Explicación

Spring Data JPA genera automáticamente:

  • SELECT

  • INSERT

  • UPDATE

  • DELETE

👉 findByUsername se crea automáticamente por convención.


🔐 9. Servicio de autenticación (CLAVE)

@Service
public class UserDetailsServiceImpl implements UserDetailsService {

    private final UsuarioRepository repo;

    public UserDetailsServiceImpl(UsuarioRepository repo) {
        this.repo = repo;
    }

    @Override
    public UserDetails loadUserByUsername(String username) {

        Usuario user = repo.findByUsername(username)
                .orElseThrow(() -> new UsernameNotFoundException("Usuario no encontrado"));

        List<GrantedAuthority> roles = user.getRoles()
                .stream()
                .map(r -> new SimpleGrantedAuthority(r.getNombre()))
                .toList();

        return new org.springframework.security.core.userdetails.User(
                user.getUsername(),
                user.getPassword(),
                user.isEnabled(),
                true, true, true,
                roles
        );
    }
}

🔍 Explicación

Spring Security llama automáticamente este método:

👉 loadUserByUsername

Lo que hace:

  1. Busca el usuario en BD

  2. Convierte roles a permisos (GrantedAuthority)

  3. Devuelve un objeto que Spring Security entiende

💡 Aquí ocurre la magia del login.


🔧 10. Configuración de seguridad

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    private final UserDetailsServiceImpl userDetailsService;

    public SecurityConfig(UserDetailsServiceImpl userDetailsService) {
        this.userDetailsService = userDetailsService;
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {

        http
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/login", "/css/**").permitAll()
                .requestMatchers("/admin/**").hasRole("ADMIN")
                .requestMatchers("/user/**").hasAnyRole("USER", "ADMIN")
                .anyRequest().authenticated()
            )
            .formLogin(form -> form
                .loginPage("/login")
                .defaultSuccessUrl("/home", true)
                .permitAll()
            )
            .logout(logout -> logout
                .logoutSuccessUrl("/login?logout")
                .permitAll()
            );

        return http.build();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

🔍 Explicación DETALLADA

🔒 authorizeHttpRequests

Define quién puede entrar:

  • /login → público

  • /admin/** → solo ADMIN

  • /user/** → USER o ADMIN


🔑 formLogin

  • Página personalizada de login

  • Redirección al iniciar sesión


🔓 logout

  • Cierra sesión

  • Redirige al login


🔐 PasswordEncoder

  • Encripta contraseñas

  • Usa BCrypt (seguro)


🎮 11. Controlador

@Controller
public class AuthController {

    @GetMapping("/login")
    public String login() {
        return "login";
    }

    @GetMapping("/home")
    public String home() {
        return "home";
    }
}

🔍 Explicación

  • /login → muestra formulario

  • /home → página protegida


🎨 12. Vista Login

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <title>Login</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
</head>

<body class="bg-light">

<div class="container d-flex justify-content-center align-items-center" style="height:100vh;">
    <div class="card p-4 shadow" style="width:350px;">

        <h3 class="text-center">Iniciar Sesión</h3>

        <form th:action="@{/login}" method="post">
            <input type="text" name="username" class="form-control mb-2" placeholder="Usuario" required>
            <input type="password" name="password" class="form-control mb-3" placeholder="Contraseña" required>

            <button class="btn btn-primary w-100">Entrar</button>
        </form>

        <div th:if="${param.error}" class="text-danger mt-2">
            Credenciales inválidas
        </div>

        <div th:if="${param.logout}" class="text-success mt-2">
            Sesión cerrada
        </div>

    </div>
</div>

</body>
</html>

🔍 Explicación

  • th:action="@{/login}" → Spring maneja el POST automáticamente

  • ${param.error} → muestra error si login falla

  • Bootstrap → diseño moderno sin CSS extra


🔑 13. Crear usuario inicial

@Bean
CommandLineRunner init(UsuarioRepository repo, PasswordEncoder encoder) {
    return args -> {
        Usuario user = new Usuario();
        user.setUsername("admin");
        user.setPassword(encoder.encode("1234"));
        user.setEnabled(true);

        Rol rol = new Rol();
        rol.setNombre("ROLE_ADMIN");

        user.setRoles(List.of(rol));

        repo.save(user);
    };
}

🔍 Explicación

  • Se ejecuta al iniciar la app

  • Inserta usuario automáticamente

  • IMPORTANTE: contraseña encriptada


🚀 14. Flujo completo del login (MUY IMPORTANTE)

  1. Usuario abre /login

  2. Ingresa credenciales

  3. Spring Security intercepta el POST

  4. Llama a UserDetailsService

  5. Valida contraseña

  6. Si es correcto → redirige a /home

  7. Si falla → vuelve a login con error


✅ Resultado final

✔ Sistema de login profesional
✔ Seguridad real con Spring Security
✔ Roles y permisos
✔ Interfaz moderna
✔ Código limpio y escalable


💡 Próximos pasos recomendados

  • Registro de usuarios

  • CRUD de usuarios

  • Panel admin

  • JWT para API REST

  • Protección CSRF avanzada


🧠 Conclusión

Este proyecto ya es una base sólida para:

  • Sistemas empresariales

  • APIs seguras

  • Aplicaciones reales

Comentarios

Entradas populares de este blog

Aprende a Armar tu PC con el Simulador de Cisco 🖥️ Guía Paso a Paso

¿Qué es XAMPP y cómo descargarlo e instalarlo en Windows?

🗄️ Cómo Instalar y usar SQLite en Windows [Guía Paso a Paso]