En este tema, aprenderemos cómo implementar la autenticación y autorización de usuarios en una aplicación Spring Boot utilizando Spring Security. La autenticación es el proceso de verificar la identidad de un usuario, mientras que la autorización determina qué recursos puede acceder el usuario autenticado.
Contenido
Introducción a la Autenticación y Autorización
Conceptos Clave
- Autenticación: Proceso de verificar la identidad de un usuario.
- Autorización: Proceso de determinar los permisos de un usuario autenticado.
- Spring Security: Un framework de seguridad para aplicaciones Java que proporciona autenticación y autorización.
Configuración Básica de Spring Security
Para empezar, necesitamos agregar la dependencia de Spring Security en nuestro archivo pom.xml
:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency>
Spring Boot automáticamente configura Spring Security con una configuración básica. Sin embargo, para personalizar la autenticación y autorización, necesitamos crear una clase de configuración.
Autenticación en Memoria
Configuración de Usuarios en Memoria
Podemos configurar usuarios en memoria para propósitos de prueba. Aquí hay un ejemplo de cómo hacerlo:
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.provisioning.InMemoryUserDetailsManager; @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.inMemoryAuthentication() .withUser("user").password("{noop}password").roles("USER") .and() .withUser("admin").password("{noop}admin").roles("ADMIN"); } @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers("/admin/**").hasRole("ADMIN") .antMatchers("/user/**").hasRole("USER") .antMatchers("/", "/home").permitAll() .and() .formLogin() .loginPage("/login") .permitAll() .and() .logout() .permitAll(); } }
Explicación del Código
@EnableWebSecurity
: Habilita la seguridad web en la aplicación.configure(AuthenticationManagerBuilder auth)
: Configura la autenticación en memoria con dos usuarios:user
yadmin
.configure(HttpSecurity http)
: Configura las reglas de autorización. Solo los usuarios con el rolADMIN
pueden acceder a las rutas/admin/**
, y los usuarios con el rolUSER
pueden acceder a las rutas/user/**
.
Autenticación con Base de Datos
Para una aplicación real, es más común autenticar usuarios contra una base de datos. Aquí hay un ejemplo de cómo hacerlo:
Configuración de la Base de Datos
Primero, necesitamos agregar las dependencias de JPA y H2 (o cualquier otra base de datos que prefieras) en el archivo pom.xml
:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>runtime</scope> </dependency>
Creación de la Entidad Usuario
import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; @Entity public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String username; private String password; private String role; // Getters and Setters }
Creación del Repositorio de Usuario
import org.springframework.data.jpa.repository.JpaRepository; public interface UserRepository extends JpaRepository<User, Long> { User findByUsername(String username); }
Configuración de la Autenticación con Base de Datos
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private UserRepository userRepository; @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(username -> { User user = userRepository.findByUsername(username); if (user == null) { throw new UsernameNotFoundException("User not found"); } return org.springframework.security.core.userdetails.User .withUsername(user.getUsername()) .password(user.getPassword()) .roles(user.getRole()) .build(); }).passwordEncoder(passwordEncoder()); } @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers("/admin/**").hasRole("ADMIN") .antMatchers("/user/**").hasRole("USER") .antMatchers("/", "/home").permitAll() .and() .formLogin() .loginPage("/login") .permitAll() .and() .logout() .permitAll(); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } }
Explicación del Código
UserRepository
: Interfaz que extiendeJpaRepository
para realizar operaciones CRUD en la entidadUser
.configure(AuthenticationManagerBuilder auth)
: Configura la autenticación utilizando elUserRepository
para cargar los detalles del usuario desde la base de datos.passwordEncoder()
: Define unPasswordEncoder
para codificar las contraseñas. En este caso, usamosBCryptPasswordEncoder
.
Autorización Basada en Roles
La autorización basada en roles se configura en el método configure(HttpSecurity http)
de la clase SecurityConfig
. Aquí especificamos qué roles tienen acceso a qué rutas.
@Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers("/admin/**").hasRole("ADMIN") .antMatchers("/user/**").hasRole("USER") .antMatchers("/", "/home").permitAll() .and() .formLogin() .loginPage("/login") .permitAll() .and() .logout() .permitAll(); }
Explicación del Código
antMatchers("/admin/**").hasRole("ADMIN")
: Solo los usuarios con el rolADMIN
pueden acceder a las rutas que comienzan con/admin/
.antMatchers("/user/**").hasRole("USER")
: Solo los usuarios con el rolUSER
pueden acceder a las rutas que comienzan con/user/
.antMatchers("/", "/home").permitAll()
: Permite el acceso a todos los usuarios a las rutas/
y/home
.
Ejercicio Práctico
Ejercicio
- Configura una base de datos H2 en tu aplicación Spring Boot.
- Crea una entidad
User
con los camposusername
,password
yrole
. - Implementa un repositorio
UserRepository
para realizar operaciones CRUD en la entidadUser
. - Configura la autenticación y autorización utilizando la base de datos H2.
- Crea una página de inicio de sesión personalizada.
Solución
-
Configuración de la Base de Datos H2:
spring.datasource.url=jdbc:h2:mem:testdb spring.datasource.driverClassName=org.h2.Driver spring.datasource.username=sa spring.datasource.password=password spring.h2.console.enabled=true spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
-
Entidad
User
:@Entity public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String username; private String password; private String role; // Getters and Setters }
-
Repositorio
UserRepository
:public interface UserRepository extends JpaRepository<User, Long> { User findByUsername(String username); }
-
Configuración de Seguridad:
@Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private UserRepository userRepository; @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(username -> { User user = userRepository.findByUsername(username); if (user == null) { throw new UsernameNotFoundException("User not found"); } return org.springframework.security.core.userdetails.User .withUsername(user.getUsername()) .password(user.getPassword()) .roles(user.getRole()) .build(); }).passwordEncoder(passwordEncoder()); } @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers("/admin/**").hasRole("ADMIN") .antMatchers("/user/**").hasRole("USER") .antMatchers("/", "/home").permitAll() .and() .formLogin() .loginPage("/login") .permitAll() .and() .logout() .permitAll(); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } }
-
Página de Inicio de Sesión Personalizada:
- Crea un archivo
login.html
en el directoriosrc/main/resources/templates
:<!DOCTYPE html> <html> <head> <title>Login</title> </head> <body> <h2>Login</h2> <form method="post" action="/login"> <div> <label>Username:</label> <input type="text" name="username"/> </div> <div> <label>Password:</label> <input type="password" name="password"/> </div> <div> <button type="submit">Login</button> </div> </form> </body> </html>
- Crea un archivo
Conclusión
En esta sección, hemos aprendido cómo implementar la autenticación y autorización de usuarios en una aplicación Spring Boot utilizando Spring Security. Hemos cubierto tanto la autenticación en memoria como la autenticación con base de datos, y hemos visto cómo configurar la autorización basada en roles. Con estos conocimientos, puedes asegurar tu aplicación y controlar el acceso a diferentes partes de tu aplicación según los roles de los usuarios.
En el próximo módulo, exploraremos cómo implementar la autenticación JWT en Spring Boot para una seguridad más avanzada y escalable.
Curso de Spring Boot
Módulo 1: Introducción a Spring Boot
- ¿Qué es Spring Boot?
- Configuración de tu Entorno de Desarrollo
- Creando tu Primera Aplicación Spring Boot
- Entendiendo la Estructura del Proyecto Spring Boot
Módulo 2: Conceptos Básicos de Spring Boot
- Anotaciones de Spring Boot
- Inyección de Dependencias en Spring Boot
- Configuración de Spring Boot
- Propiedades de Spring Boot
Módulo 3: Construyendo Servicios Web RESTful
- Introducción a los Servicios Web RESTful
- Creando Controladores REST
- Manejo de Métodos HTTP
- Manejo de Excepciones en REST
Módulo 4: Acceso a Datos con Spring Boot
- Introducción a Spring Data JPA
- Configuración de Fuentes de Datos
- Creación de Entidades JPA
- Uso de Repositorios de Spring Data
- Métodos de Consulta en Spring Data JPA
Módulo 5: Seguridad en Spring Boot
- Introducción a Spring Security
- Configuración de Spring Security
- Autenticación y Autorización de Usuarios
- Implementación de Autenticación JWT
Módulo 6: Pruebas en Spring Boot
- Introducción a las Pruebas
- Pruebas Unitarias con JUnit
- Pruebas de Integración
- Simulación con Mockito
Módulo 7: Funciones Avanzadas de Spring Boot
Módulo 8: Despliegue de Aplicaciones Spring Boot
Módulo 9: Rendimiento y Monitoreo
- Ajuste de Rendimiento
- Monitoreo con Spring Boot Actuator
- Uso de Prometheus y Grafana
- Gestión de Registros y Logs