En este tema, aprenderemos cómo configurar Spring Security en una aplicación Spring Boot. Spring Security es un marco poderoso y altamente personalizable para la autenticación y autorización en aplicaciones Java. A lo largo de esta sección, cubriremos los conceptos básicos de la configuración de Spring Security, incluyendo la configuración de seguridad básica, la personalización de la autenticación y la autorización, y la integración con bases de datos para la gestión de usuarios.
Contenido
Configuración Básica de Spring Security
Paso 1: Añadir Dependencias
Para empezar, necesitamos añadir las dependencias de Spring Security en nuestro archivo pom.xml
:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency>
Paso 2: Configuración por Defecto
Spring Boot proporciona una configuración de seguridad por defecto que protege todas las rutas de nuestra aplicación. Al añadir la dependencia de Spring Security, nuestra aplicación requerirá autenticación para todas las rutas.
Paso 3: Configuración Personalizada
Podemos personalizar la configuración de seguridad creando una clase que extienda WebSecurityConfigurerAdapter
:
import org.springframework.context.annotation.Configuration; 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; @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers("/public/**").permitAll() // Permitir acceso sin autenticación a rutas públicas .anyRequest().authenticated() // Requerir autenticación para cualquier otra ruta .and() .formLogin() .loginPage("/login") // Página de inicio de sesión personalizada .permitAll() .and() .logout() .permitAll(); } }
Explicación del Código
@Configuration
y@EnableWebSecurity
: Anotaciones que indican que esta clase es una configuración de seguridad.authorizeRequests()
: Método para definir las reglas de autorización.antMatchers("/public/**").permitAll()
: Permite el acceso sin autenticación a cualquier ruta que comience con/public/
.anyRequest().authenticated()
: Requiere autenticación para cualquier otra ruta.formLogin()
: Habilita el formulario de inicio de sesión.loginPage("/login")
: Especifica una página de inicio de sesión personalizada.logout()
: Habilita la funcionalidad de cierre de sesión.
Personalización de la Autenticación
Paso 1: Configuración de Usuarios en Memoria
Podemos configurar usuarios en memoria para propósitos de prueba:
import org.springframework.context.annotation.Bean; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; 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 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"); } }
Explicación del Código
auth.inMemoryAuthentication()
: Configura la autenticación en memoria.withUser("user").password("{noop}password").roles("USER")
: Añade un usuario con el rolUSER
.{noop}
: Indica que la contraseña no está codificada.
Personalización de la Autorización
Paso 1: Configuración de Roles y Permisos
Podemos definir roles y permisos específicos para diferentes rutas:
@Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers("/admin/**").hasRole("ADMIN") // Solo usuarios con rol ADMIN pueden acceder .antMatchers("/user/**").hasRole("USER") // Solo usuarios con rol USER pueden acceder .antMatchers("/public/**").permitAll() // Permitir acceso sin autenticación a rutas públicas .anyRequest().authenticated() .and() .formLogin() .loginPage("/login") .permitAll() .and() .logout() .permitAll(); }
Explicación del Código
antMatchers("/admin/**").hasRole("ADMIN")
: Solo usuarios con el rolADMIN
pueden acceder a rutas que comiencen con/admin/
.antMatchers("/user/**").hasRole("USER")
: Solo usuarios con el rolUSER
pueden acceder a rutas que comiencen con/user/
.
Integración con Bases de Datos
Paso 1: Configuración de la Fuente de Datos
Primero, configuramos la fuente de datos en application.properties
:
spring.datasource.url=jdbc:mysql://localhost:3306/mydb spring.datasource.username=root spring.datasource.password=root spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
Paso 2: Configuración de Usuarios en la Base de Datos
Creamos una tabla de usuarios y roles en la base de datos:
CREATE TABLE users ( username VARCHAR(50) NOT NULL PRIMARY KEY, password VARCHAR(100) NOT NULL, enabled BOOLEAN NOT NULL ); CREATE TABLE authorities ( username VARCHAR(50) NOT NULL, authority VARCHAR(50) NOT NULL, FOREIGN KEY (username) REFERENCES users(username) );
Paso 3: Configuración de la Autenticación con JDBC
Configuramos la autenticación para usar JDBC:
import javax.sql.DataSource; import org.springframework.beans.factory.annotation.Autowired; 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; @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private DataSource dataSource; @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.jdbcAuthentication() .dataSource(dataSource) .usersByUsernameQuery("SELECT username, password, enabled FROM users WHERE username = ?") .authoritiesByUsernameQuery("SELECT username, authority FROM authorities WHERE username = ?"); } @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers("/public/**").permitAll() .anyRequest().authenticated() .and() .formLogin() .loginPage("/login") .permitAll() .and() .logout() .permitAll(); } }
Explicación del Código
auth.jdbcAuthentication()
: Configura la autenticación para usar JDBC.dataSource(dataSource)
: Especifica la fuente de datos.usersByUsernameQuery
: Consulta SQL para obtener los detalles del usuario.authoritiesByUsernameQuery
: Consulta SQL para obtener los roles del usuario.
Ejercicios Prácticos
Ejercicio 1: Configuración Básica de Seguridad
- Añade la dependencia de Spring Security a tu proyecto.
- Crea una clase de configuración de seguridad que permita el acceso sin autenticación a la ruta
/public/**
y requiera autenticación para cualquier otra ruta.
Ejercicio 2: Configuración de Usuarios en Memoria
- Configura dos usuarios en memoria: uno con el rol
USER
y otro con el rolADMIN
. - Configura las rutas
/user/**
y/admin/**
para que solo sean accesibles por usuarios con los roles correspondientes.
Ejercicio 3: Integración con Base de Datos
- Configura una base de datos MySQL para almacenar usuarios y roles.
- Configura la autenticación en tu aplicación Spring Boot para usar la base de datos.
Soluciones
Ejercicio 1
import org.springframework.context.annotation.Configuration; 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; @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers("/public/**").permitAll() .anyRequest().authenticated() .and() .formLogin() .permitAll() .and() .logout() .permitAll(); } }
Ejercicio 2
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; @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("/public/**").permitAll() .anyRequest().authenticated() .and() .formLogin() .permitAll() .and() .logout() .permitAll(); } }
Ejercicio 3
import javax.sql.DataSource; import org.springframework.beans.factory.annotation.Autowired; 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; @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private DataSource dataSource; @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.jdbcAuthentication() .dataSource(dataSource) .usersByUsernameQuery("SELECT username, password, enabled FROM users WHERE username = ?") .authoritiesByUsernameQuery("SELECT username, authority FROM authorities WHERE username = ?"); } @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers("/public/**").permitAll() .anyRequest().authenticated() .and() .formLogin() .permitAll() .and() .logout() .permitAll(); } }
Conclusión
En esta sección, hemos aprendido cómo configurar Spring Security en una aplicación Spring Boot. Hemos cubierto la configuración básica, la personalización de la autenticación y la autorización, y la integración con bases de datos. Con estos conocimientos, puedes asegurar tu aplicación Spring Boot y proteger tus rutas y recursos de manera efectiva. En el próximo tema, exploraremos cómo implementar la autenticación JWT en Spring Boot.
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