SpringBoot
Tutorial SpringBoot: Seguridad basada en formulario en MVC con Thymeleaf
Programar seguridad con Spring Security basada en formulario login y logout personalizado en Spring Web en controladores MVC con Spring Boot y Thymeleaf.
Aprende SpringBoot GRATIS y certifícateConfiguración entidades
Para gestionar la autenticación y autorización en nuestra aplicación de Spring Boot 3, es esencial configurar las entidades User y Role con una relación ManyToMany. Además, estableceremos la entidad BlogPost (o Producto) con un campo author que referencia a un User utilizando Lombok para simplificar nuestro código.
Comenzamos definiendo la entidad Role, que representará los roles de usuario dentro del sistema, como "ADMIN" o "USER". Utilizamos las anotaciones de JPA para mapear la entidad a una tabla en la base de datos y Lombok para generar automáticamente los métodos getters, setters y constructores necesarios:
@Entity
@Table(name = "roles")
@Getter
@Setter
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class Role {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, unique = true)
private String name;
@ManyToMany(mappedBy = "roles")
private Set<User> users = new HashSet<>();
}
La relación ManyToMany con la entidad User se establece con @ManyToMany(mappedBy = "roles")
, indicando que el propietario de la relación es la entidad User.
La entidad User se define de la siguiente manera:
@Entity
@Table(name = "users")
@Getter
@Setter
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, unique = true)
private String username;
@Column(nullable = false)
private String password;
@ToString.Exclude
@ManyToMany
@JoinTable(
name = "users_roles",
joinColumns = @JoinColumn(name = "user_id"),
inverseJoinColumns = @JoinColumn(name = "role_id"))
private Set<Role> roles = new HashSet<>();
}
Aquí, la relación ManyToMany se configura explícitamente con @JoinTable
, especificando la tabla intermedia users_roles y las columnas de unión correspondientes. El uso de Set<Role>
en lugar de List<Role>
evita duplicados y mejora el rendimiento en ciertas operaciones. La anotación @Column
con unique = true
en username asegura que no existan usuarios duplicados en la base de datos.
Para asociar un User como autor de un BlogPost, definimos la entidad BlogPost con una relación ManyToOne hacia User:
@Entity
@Table(name = "blog_posts")
@Getter
@Setter
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class BlogPost {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String title;
@Lob
private String content;
@ManyToOne(optional = false)
@JoinColumn(name = "author_id")
private User author;
}
La anotación @ManyToOne(optional = false)
indica que cada BlogPost debe tener obligatoriamente un author asociado. El campo @Lob
en content permite almacenar textos de gran tamaño, ideal para el contenido del blog. Usando Lombok, mantenemos el código conciso y legible, sin necesidad de escribir manualmente los métodos básicos.
Es importante manejar correctamente las relaciones bidireccionales para evitar problemas de rendimiento y referencialidad. Por ejemplo, en la entidad User, podemos agregar un conjunto de BlogPosts para reflejar la relación inversa:
@OneToMany(mappedBy = "author", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<BlogPost> blogPosts = new HashSet<>();
De esta forma, podemos acceder fácilmente a los BlogPosts de un usuario y mantener la integridad de los datos. La opción cascade = CascadeType.ALL
asegura que las operaciones realizadas sobre el usuario se propaguen a sus BlogPosts, y orphanRemoval = true
elimina los BlogPosts huérfanos cuando un usuario es eliminado.
Al utilizar Lombok, también podemos crear constructores personalizados si necesitamos inicializar ciertos campos de manera específica. Por ejemplo:
@RequiredArgsConstructor
public class User {
// campos...
public User(String username, String password) {
this.username = username;
this.password = password;
}
}
La anotación @RequiredArgsConstructor
genera un constructor con los atributos marcados como final
o con restricciones como @NonNull
. Esto nos ayuda a garantizar que ciertos campos importantes nunca sean nulos, mejorando la consistencia de la entidad.
Para completar la configuración, es recomendable implementar la interfaz UserDetails
en nuestra entidad User para integrarla con Spring Security:
@Entity
@Table(name = "users")
@Getter
@Setter
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class User implements UserDetails {
// campos...
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return roles.stream()
.map(role -> new SimpleGrantedAuthority(role.getName()))
.collect(Collectors.toSet());
}
@Override
public String getPassword() {
return password;
}
@Override
public String getUsername() {
return username;
}
// Otros métodos de UserDetails...
}
Al implementar los métodos de UserDetails
, proporcionamos a Spring Security la información requerida para manejar la autenticación y la autorización de usuarios en el sistema. Los roles se transforman en instancias de GrantedAuthority
, que son utilizados por Spring Security para controlar el acceso a los recursos.
Finalmente, es crucial realizar la persistencia de estas entidades utilizando repositorios de Spring Data JPA. Por ejemplo, podemos crear un repositorio para la entidad User:
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByUsername(String username);
}
Este repositorio nos permite buscar usuarios por su nombre de usuario, lo cual es esencial durante el proceso de autenticación. La utilización de Optional es una buena práctica para manejar la posible ausencia de resultados y evitar NullPointerExceptions.
Implementar UserDetailsService con Spring Data JPA
Para integrar nuestras entidades User y Role con Spring Security, es necesario implementar la interfaz UserDetailsService. Esta interfaz proporciona un método para cargar información específica del usuario durante el proceso de autenticación.
Comenzamos creando una clase que implemente UserDetailsService, donde utilizaremos nuestro UserRepository para obtener los usuarios desde la base de datos:
@Service
public class CustomUserDetailsService implements UserDetailsService {
private final UserRepository userRepository;
public CustomUserDetailsService(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return userRepository.findByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException("Usuario no encontrado: " + username));
}
}
Esta clase está anotada con @Service
para que Spring la detecte como un componente gestionado. El constructor inyecta el UserRepository utilizando inyección de dependencias. En el método loadUserByUsername
, buscamos el usuario por su nombre de usuario y, si no se encuentra, lanzamos una excepción UsernameNotFoundException.
Es importante que nuestra entidad User implemente la interfaz UserDetails para que pueda ser utilizada por Spring Security. Ya hemos implementado UserDetails en la entidad User previamente, incluyendo los métodos necesarios como getAuthorities()
, getPassword()
y getUsername()
. Esto permite que la autenticación utilice directamente nuestra entidad User.
Necesitemos definir un PasswordEncoder para manejar el cifrado de las contraseñas:
@Configuration
public class SecurityConfig {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
Configuramos dentro de SecurityConfig el SecurityFilterChain:
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authorize -> authorize
.anyRequest().authenticated()
)
.formLogin(form -> form
.loginPage("/login")
.permitAll()
);
return http.build();
}
En esta configuración, indicamos que todas las solicitudes requieren autenticación y especificamos que usaremos un formulario de inicio de sesión personalizado en /login
.
Es recomendable inicializar algunos usuarios y roles en nuestra base de datos para probar el sistema de autenticación. Podemos hacerlo creando una clase de inicialización de datos:
@Component
public class DataInitializer implements CommandLineRunner {
private final UserRepository userRepository;
private final RoleRepository roleRepository;
private final PasswordEncoder passwordEncoder;
public DataInitializer(UserRepository userRepository, RoleRepository roleRepository, PasswordEncoder passwordEncoder) {
this.userRepository = userRepository;
this.roleRepository = roleRepository;
this.passwordEncoder = passwordEncoder;
}
@Override
public void run(String... args) {
Role roleUser = new Role();
roleUser.setName("ROLE_USER");
roleRepository.save(roleUser);
Role roleAdmin = new Role();
roleAdmin.setName("ROLE_ADMIN");
roleRepository.save(roleAdmin);
User user = new User();
user.setUsername("usuario");
user.setPassword(passwordEncoder.encode("contraseña"));
user.getRoles().add(roleUser);
userRepository.save(user);
User admin = new User();
admin.setUsername("administrador");
admin.setPassword(passwordEncoder.encode("contraseña"));
admin.getRoles().add(roleUser);
admin.getRoles().add(roleAdmin);
userRepository.save(admin);
}
}
En esta clase, anotada con @Component
, utilizamos el método run
de CommandLineRunner para insertar datos iniciales. Creamos roles y usuarios, asignamos roles a los usuarios y codificamos las contraseñas con passwordEncoder.
Es esencial verificar que las contraseñas se almacenen codificadas en la base de datos. De esta manera, garantizamos la seguridad de las credenciales y cumplimos con buenas prácticas de desarrollo.
Con todo configurado, al iniciar la aplicación, podremos acceder a la página de inicio de sesión personalizada y autenticarnos con los usuarios creados. Spring Security utilizará nuestra implementación de UserDetailsService para cargar los detalles del usuario y verificar las credenciales.
Si necesitamos personalizar aún más la autenticación, podemos implementar métodos adicionales en nuestra clase CustomUserDetailsService o ajustar la configuración en SecurityConfig. Por ejemplo, podríamos manejar usuarios bloqueados, expiración de cuentas o añadir campos adicionales de autenticación.
Configuración de SecurityFilterChain
En esta sección, configuraremos la seguridad global basada en formulario, permitiendo que los usuarios se autentiquen a través de una página de inicio de sesión personalizada.
El siguiente paso es definir el SecurityFilterChain en profundidad, donde configuraremos las reglas de seguridad y especificaremos cómo se manejarán las peticiones HTTP:
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authorize -> authorize
.requestMatchers("/login", "/css/**", "/js/**").permitAll()
.anyRequest().authenticated()
)
.formLogin(form -> form
.loginPage("/login")
.defaultSuccessUrl("/home", true)
.permitAll()
)
.logout(logout -> logout
.logoutUrl("/logout")
.logoutSuccessUrl("/login?logout")
.permitAll()
);
return http.build();
}
En esta configuración, hemos definido varios aspectos clave:
- Autorizaciones de solicitudes: Permitimos el acceso sin autenticación a la página de /login y a los recursos estáticos como /css/ y /js/. Todas las demás solicitudes requieren autenticación.
- Inicio de sesión basado en formulario: Especificamos que la página de inicio de sesión personalizada se encuentra en /login, y definimos la URL de redirección tras un inicio de sesión exitoso con
defaultSuccessUrl("/home", true)
. - Cierre de sesión: Configuramos la URL para cerrar la sesión en /logout y definimos la redirección tras un cierre de sesión exitoso.
Es importante destacar que hemos utilizado la API fluida de HttpSecurity para establecer nuestras configuraciones de manera clara y legible.
Con esta configuración, nuestra aplicación ya está protegida por Spring Security, y los usuarios deberán autenticarse para acceder a los recursos protegidos. Los intentos de acceso sin autenticación a rutas protegidas redirigirán al usuario a la página de inicio de sesión.
Es posible que también necesitemos configurar excepciones para permitir el acceso a recursos como imágenes o fuentes. Podemos ajustar el método authorizeHttpRequests
para incluir estas rutas:
.requestMatchers("/images/**", "/webjars/**").permitAll()
Asimismo, si estamos utilizando Thymeleaf, es recomendable configurar el manejo de recursos estáticos correctamente.
Para mejorar la experiencia del usuario, podemos personalizar los mensajes de error en el inicio de sesión, manejando casos como credenciales incorrectas o cuentas bloqueadas. Esto se logra agregando parámetros en la configuración de formLogin
:
.failureUrl("/login?error=true")
De esta manera, redirigiremos al usuario nuevamente a la página de inicio de sesión en caso de error, y podremos mostrar mensajes adecuados.
También es posible configurar la gestión de sesiones para controlar el comportamiento de las sesiones de usuario:
.sessionManagement(session -> session
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
.invalidSessionUrl("/login?invalid-session=true")
.maximumSessions(1)
.maxSessionsPreventsLogin(true)
)
Con estas opciones, limitamos a un solo inicio de sesión por usuario y manejamos sesiones inválidas apropiadamente.
Vistas login y logout personalizadas con Thymeleaf
Una vez configurada la seguridad en nuestra aplicación Spring Boot 3, es fundamental ofrecer una experiencia de usuario coherente mediante vistas personalizadas de login y logout utilizando Thymeleaf. Esto permite mantener la identidad visual de la aplicación y proporcionar mensajes y elementos adaptados a nuestras necesidades.
Para crear una vista de login personalizada, comenzamos por diseñar una plantilla Thymeleaf que represente el formulario de inicio de sesión. Creamos el archivo login.html
dentro de la carpeta templates
:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Iniciar Sesión</title>
<link rel="stylesheet" th:href="@{/css/style.css}">
</head>
<body>
<h1>Por favor, inicia sesión</h1>
<form th:action="@{/login}" method="post">
<div>
<label for="username">Usuario:</label>
<input type="text" id="username" name="username" autofocus>
</div>
<div>
<label for="password">Contraseña:</label>
<input type="password" id="password" name="password">
</div>
<div>
<input type="checkbox" id="remember-me" name="remember-me">
<label for="remember-me">Recordarme</label>
</div>
<div>
<button type="submit">Entrar</button>
</div>
<div th:if="${param.error}">
<p class="error">Nombre de usuario o contraseña incorrectos</p>
</div>
<div th:if="${param.logout}">
<p class="message">Has cerrado sesión correctamente</p>
</div>
</form>
</body>
</html>
En esta plantilla, utilizamos la expresión Thymeleaf th:action="@{/login}"
para indicar que el formulario enviará los datos a la ruta /login
. Esto es coherente con la configuración de seguridad que maneja la autenticación. Es importante incluir los campos username
y password
, ya que Spring Security los espera con esos nombres por defecto.
Además, gestionamos los mensajes de error y de cierre de sesión exitoso mediante las condiciones th:if="${param.error}"
y th:if="${param.logout}"
. Estos parámetros son añadidos automáticamente por Spring Security cuando ocurre un error de autenticación o cuando el usuario cierra sesión.
Para estilizar el formulario y mejorar la usabilidad, podemos enlazar una hoja de estilos CSS personalizada a través de th:href="@{/css/style.css}"
.
El siguiente paso es asegurarnos de que nuestra configuración de seguridad reconoce esta plantilla como la página de inicio de sesión. En la clase SecurityConfig
, debemos haber especificado la ruta de la página de login personalizada:
.formLogin(form -> form
.loginPage("/login")
.defaultSuccessUrl("/home", true)
.permitAll()
)
Con esta configuración, Spring Security redirige a los usuarios no autenticados a /login
cuando intentan acceder a recursos protegidos. Además, permitAll()
permite el acceso sin autenticación a la página de login.
Es posible que necesitemos crear un controlador que maneje la solicitud GET a /login
. Aunque Spring Security maneja las solicitudes POST del formulario de login automáticamente, la ruta GET para mostrar la página de login debe ser manejada por un controlador:
@Controller
public class LoginController {
@GetMapping("/login")
public String login() {
return "login";
}
}
Este controlador sencillo devuelve la vista login.html
cuando se accede a /login
mediante una solicitud GET. De esta manera, los usuarios pueden ver el formulario de inicio de sesión personalizado.
Si deseamos personalizar aún más el proceso de autenticación, como cambiar los nombres de los campos del formulario o la URL de procesamiento del login, podemos configurar estos detalles en formLogin
:
.formLogin(form -> form
.loginPage("/login")
.loginProcessingUrl("/perform_login")
.usernameParameter("user")
.passwordParameter("pass")
.defaultSuccessUrl("/home", true)
.permitAll()
)
En este caso, debemos ajustar el formulario en login.html
para que los campos tengan los nuevos nombres:
<input type="text" id="user" name="user" autofocus>
<input type="password" id="pass" name="pass">
<form th:action="@{/perform_login}" method="post">
Sin embargo, es recomendable utilizar los nombres por defecto (username
y password
) para simplificar la configuración.
Para implementar una vista personalizada de logout, podemos añadir un enlace en el menú o en alguna parte de la aplicación donde el usuario autenticado pueda cerrar sesión:
<a th:href="@{/logout}">Cerrar Sesión</a>
Por defecto, Spring Security maneja las solicitudes POST a /logout
para cerrar sesión. Si deseamos que el cierre de sesión se realice mediante una solicitud GET (por ejemplo, al hacer clic en un enlace), debemos ajustar la configuración de seguridad:
.logout(logout -> logout
.logoutRequestMatcher(new AntPathRequestMatcher("/logout", "GET"))
.logoutSuccessUrl("/login?logout")
.permitAll()
)
Sin embargo, por motivos de seguridad, se recomienda utilizar solicitudes POST para acciones sensibles como el cierre de sesión. Una alternativa es utilizar JavaScript para enviar una solicitud POST cuando el usuario hace clic en el enlace.
Si queremos mostrar una página personalizada tras el cierre de sesión, podemos crear una vista logout.html
y ajustar la configuración:
.logout(logout -> logout
.logoutSuccessUrl("/logout-success")
)
Y en el controlador:
@Controller
public class LogoutController {
@GetMapping("/logout-success")
public String logoutSuccess() {
return "logout-success";
}
}
Además, en nuestra plantilla de Thymeleaf, podemos mostrar el nombre del usuario autenticado utilizando la expresión #authentication
:
<p>Bienvenido, <span th:text="${#authentication.name}"></span></p>
De este modo, mejoramos la experiencia de usuario al mostrar información relevante y dinámica.
Control de acceso en vistas de thymeleaf
Después de haber configurado la seguridad en nuestra aplicación y personalizado las vistas de login y logout, es importante manejar el control de acceso en nuestras vistas de Thymeleaf. Esto nos permite mostrar u ocultar elementos en las páginas dependiendo de los roles o privilegios del usuario autenticado. Además, es necesario asignar el usuario autenticado como autor o responsable al crear nuevas entidades, como es el caso de la entidad Producto.
Para controlar el acceso en las vistas, Thymeleaf proporciona un dialecto de seguridad que facilita este proceso. Primero, asegurémonos de que el dialecto de Spring Security esté incluido en nuestra aplicación. Normalmente, esto se logra mediante la dependencia correspondiente en el archivo build.gradle
o pom.xml
:
<!-- Dependencia para Maven -->
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity6</artifactId>
</dependency>
Una vez incluido el dialecto, es opcional registrarlo en nuestra configuración de Thymeleaf. Si estamos utilizando Spring Boot, esto suele ser automático. Sin embargo, si contamos con una configuración manual, debemos añadir el dialecto:
@Bean
public SpringTemplateEngine templateEngine() {
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.setTemplateResolver(templateResolver());
templateEngine.addDialect(new SpringSecurityDialect());
return templateEngine;
}
Con el dialecto de Spring Security habilitado, podemos utilizar las etiquetas de seguridad en nuestras vistas de Thymeleaf. Por ejemplo, si queremos mostrar un enlace solamente a usuarios con el rol ADMIN, podemos hacerlo de la siguiente manera:
<nav>
<ul>
<li><a th:href="@{/}" >Inicio</a></li>
<li sec:authorize="hasRole('ADMIN')">
<a th:href="@{/productos/nuevo}" >Crear Producto</a>
</li>
</ul>
</nav>
Aquí, la etiqueta sec:authorize
verifica si el usuario tiene el rol ADMIN antes de renderizar el elemento HTML correspondiente. De esta forma, los usuarios sin dicho rol no verán el enlace para crear un nuevo producto, mejorando la seguridad y la experiencia del usuario.
Además de controlar la visualización de elementos, también podemos acceder a la información del usuario autenticado en las vistas. Por ejemplo, para mostrar el nombre de usuario en la página:
<div>
<p>Bienvenido, <span sec:authentication="name"></span></p>
</div>
La expresión sec:authentication="name"
permite obtener el nombre de usuario desde el contexto de autenticación. Esto es útil para personalizar el contenido y ofrecer una interacción más dinámica.
Al crear una nueva entidad Producto, es fundamental asignar el usuario autenticado como creador o autor del producto. Para lograr esto en el controlador, necesitamos acceder al usuario actual. En Spring MVC, podemos obtener el usuario autenticado mediante el objeto Principal
o utilizando la anotación @AuthenticationPrincipal
.
Por ejemplo, en el controlador de productos:
@PostMapping("/productos")
public String guardarProducto(@ModelAttribute Producto producto, @AuthenticationPrincipal User usuarioAutenticado) {
producto.setAutor(usuarioAutenticado);
productoServicio.guardar(producto);
return "redirect:/productos";
}
En este código, el parámetro @AuthenticationPrincipal User usuarioAutenticado
inyecta el usuario actualmente autenticado. Asumimos que la clase User
es nuestra entidad de usuario que implementa UserDetails
. A continuación, asignamos el usuario como autor del producto antes de guardarlo.
Es importante asegurarse de que la entidad Producto tiene una relación adecuada con la entidad User. Por ejemplo, en la clase Producto:
@Entity
public class Producto {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String nombre;
private BigDecimal precio;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "autor_id")
private User autor;
// Getters y setters
}
La anotación @ManyToOne
establece la relación con el usuario creador del producto. De esta manera, cada producto está asociado a un usuario, lo que permite controlar permisos y realizar auditorías.
En algunos casos, es posible que necesitemos acceder al usuario autenticado dentro de servicios u otros componentes. Para ello, podemos utilizar la clase SecurityContextHolder
de Spring Security:
User usuarioAutenticado = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
Sin embargo, es recomendable utilizar la inyección directa en el controlador mediante @AuthenticationPrincipal
para mantener el código más limpio y legible.
En las vistas, podemos comprobar si el usuario tiene ciertos roles o permisos y ajustar la interfaz en consecuencia. Por ejemplo, para mostrar opciones de edición solo al autor o a administradores:
<div sec:authorize="hasRole('ADMIN') or principal.username == ${producto.autor.username}">
<a th:href="@{'/productos/editar/' + ${producto.id}}">Editar</a>
<a th:href="@{'/productos/eliminar/' + ${producto.id}}" onclick="return confirm('¿Estás seguro?');">Eliminar</a>
</div>
La expresión principal.username
nos permite acceder al nombre de usuario autenticado y compararlo con el autor del producto. De esta forma, solo el autor del producto o un administrador puede ver los enlaces de edición y eliminación.
Ejemplo de usos de etiquetas thymeleaf para seguridad en barra de navegación navbar con bootstrap 5:
<!doctype html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<body>
<nav th:fragment="nav1" class="navbar navbar-expand-lg bg-light mb-5">
<div class="container-fluid">
<a class="navbar-brand" href="/foods">
<img class="img-fluid nav-logo" src="/img/bookstore.png" alt="Logo">
</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse"
data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent"
aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link" th:href="@{/foods}">Foods</a>
</li>
<li class="nav-item">
<a class="nav-link" th:href="@{/supermarkets}">Supermarkets</a>
</li>
</ul>
<div class="d-flex" sec:authorize="isAnonymous()">
<a class="btn btn-link" th:href="@{/login}">Iniciar sesión</a>
</div>
<div class="d-flex" sec:authorize="isAnonymous()">
<a class="btn btn-link" th:href="@{/user/registration}">Registro</a>
</div>
<div class="d-flex" sec:authorize="isAuthenticated()">
<span sec:authentication="principal.username"></span>
</div>
<div class="d-flex" sec:authorize="isAuthenticated()">
<a class="btn btn-link" th:href="@{/logout}">Cerrar sesión</a>
</div>
</div>
</div>
</nav>
</body>
</html>
Es importante tener en cuenta que el control de acceso en las vistas es una medida adicional y no sustituye a la seguridad en el lado del servidor. Siempre debemos validar los permisos en el backend para evitar accesos no autorizados manipulando las solicitudes.
Para finalizar, recordemos que la gestión de permisos y roles es esencial para mantener una aplicación segura y funcional. Al combinar las etiquetas de seguridad en Thymeleaf con las anotaciones en controladores y el manejo adecuado del usuario autenticado, podemos implementar un control de acceso robusto y eficiente en nuestra aplicación Spring Boot.
Ejercicios de esta lección Seguridad basada en formulario en MVC con Thymeleaf
Evalúa tus conocimientos de esta lección Seguridad basada en formulario en MVC con Thymeleaf con nuestros retos de programación de tipo Test, Puzzle, Código y Proyecto con VSCode, guiados por IA.
API Query By Example (QBE)
Identificadores y relaciones JPA
Borrar datos de base de datos
Web y Test Starters
Métodos find en repositorios
Controladores Spring MVC
Inserción de datos
CRUD Customers Spring MVC + Spring Data JPA
Backend API REST con Spring Boot
Controladores Spring REST
Uso de Spring con Thymeleaf
API Specification
Registro de usuarios
Crear entidades JPA
Asociaciones en JPA
Asociaciones de entidades JPA
Integración con Vue
Consultas JPQL
Open API y cómo agregarlo en Spring Boot
Uso de Controladores REST
Repositorios reactivos
Inyección de dependencias
Introducción a Spring Boot
CRUD y JPA Repository
Inyección de dependencias
Vista en Spring MVC con Thymeleaf
Servicios en Spring
Operadores Reactivos
Configuración de Vue
Entidades JPA
Integración con Angular
API Specification
API Query By Example (QBE)
Controladores MVC
Anotaciones y mapeo en JPA
Consultas JPQL con @Query en Spring Data JPA
Repositorios Spring Data
Inyección de dependencias
Data JPA y Mail Starters
Configuración de Angular
Controladores Spring REST
Configuración de Controladores MVC
Consultas JPQL con @Query en Spring Data JPA
Actualizar datos de base de datos
Verificar token JWT en peticiones
Login de usuarios
Integración con React
Configuración de React
Todas las lecciones de SpringBoot
Accede a todas las lecciones de SpringBoot y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.
Introducción A Spring Boot
Introducción Y Entorno
Spring Boot Starters
Introducción Y Entorno
Inyección De Dependencias
Introducción Y Entorno
Controladores Spring Mvc
Spring Web
Vista En Spring Mvc Con Thymeleaf
Spring Web
Controladores Spring Rest
Spring Web
Open Api Y Cómo Agregarlo En Spring Boot
Spring Web
Servicios En Spring
Spring Web
Clientes Resttemplate Y Restclient
Spring Web
Rxjava En Spring Web
Spring Web
Crear Entidades Jpa
Persistencia Spring Data
Asociaciones De Entidades Jpa
Persistencia Spring Data
Repositorios Spring Data
Persistencia Spring Data
Métodos Find En Repositorios
Persistencia Spring Data
Inserción De Datos
Persistencia Spring Data
Actualizar Datos De Base De Datos
Persistencia Spring Data
Borrar Datos De Base De Datos
Persistencia Spring Data
Consultas Jpql Con @Query En Spring Data Jpa
Persistencia Spring Data
Api Query By Example (Qbe)
Persistencia Spring Data
Api Specification
Persistencia Spring Data
Repositorios Reactivos
Persistencia Spring Data
Introducción E Instalación De Apache Kafka
Mensajería Asíncrona
Crear Proyecto Con Apache Kafka
Mensajería Asíncrona
Creación De Producers
Mensajería Asíncrona
Creación De Consumers
Mensajería Asíncrona
Kafka Streams En Spring Boot
Mensajería Asíncrona
Introducción A Spring Webflux
Reactividad Webflux
Spring Data R2dbc
Reactividad Webflux
Controlador Rest Reactivo Basado En Anotaciones
Reactividad Webflux
Controlador Rest Reactivo Funcional
Reactividad Webflux
Operadores Reactivos Básicos
Reactividad Webflux
Operadores Reactivos Avanzados
Reactividad Webflux
Cliente Reactivo Webclient
Reactividad Webflux
Introducción A Spring Security
Seguridad Con Spring Security
Seguridad Basada En Formulario En Mvc Con Thymeleaf
Seguridad Con Spring Security
Registro De Usuarios
Seguridad Con Spring Security
Login De Usuarios
Seguridad Con Spring Security
Verificar Token Jwt En Peticiones
Seguridad Con Spring Security
Seguridad Jwt En Api Rest Spring Web
Seguridad Con Spring Security
Seguridad Jwt En Api Rest Reactiva Spring Webflux
Seguridad Con Spring Security
Autenticación Y Autorización Con Anotaciones
Seguridad Con Spring Security
Testing Unitario De Componentes Y Servicios
Testing Con Spring Test
Testing De Repositorios Spring Data Jpa
Testing Con Spring Test
Testing Controladores Spring Mvc Con Thymeleaf
Testing Con Spring Test
Testing Controladores Rest Con Json
Testing Con Spring Test
Testing De Aplicaciones Reactivas Webflux
Testing Con Spring Test
Testing De Seguridad Spring Security
Testing Con Spring Test
Testing Con Apache Kafka
Testing Con Spring Test
Integración Con Angular
Integración Frontend
Integración Con React
Integración Frontend
Integración Con Vue
Integración Frontend
En esta lección
Objetivos de aprendizaje de esta lección
- Incorporar Spring Security en aplicaciones MVC
- Manejar la seguridad en backend
- Manejar la seguridad en vistas HTML de Thymeleaf
- Control de acceso y detección del usuario autenticado