Spring Security proporciona un mecanismo potente para configurar la seguridad mediante anotaciones. Esto hace que nuestro código sea no solo más compacto, sino también más legible. Las anotaciones permiten delimitar el acceso a recursos y métodos justo donde se usan.
En esta lección veremos cuatro anotaciones principales:
@Secured@RolesAllowed@PreAuthorize@PostAuthorize
Anotación @Secured
La anotación @Secured define los roles a los que se les permite acceder a un método o recurso. Es simple y cómoda si necesitas una forma rápida de restringir el acceso basándote en roles.
@Secured comprueba si el usuario tiene un rol determinado (por ejemplo, ROLE_USER) para poder invocar ese método.
Ejemplo:
import org.springframework.security.access.annotation.Secured;
import org.springframework.stereotype.Service;
@Service
public class MyService {
@Secured("ROLE_ADMIN")
public void adminOnlyMethod() {
System.out.println("¡Este método está disponible solo para administradores!");
}
@Secured({"ROLE_USER", "ROLE_ADMIN"})
public void userOrAdminMethod() {
System.out.println("Este método está disponible para usuarios y administradores.");
}
}
@EnableGlobalMethodSecurity con el parámetro
securedEnabled = true.
¿Cómo activarlo? Añade la siguiente configuración en la clase de configuración de seguridad:
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true) // Activamos el soporte para @Secured
public class SecurityConfig {
// Aquí puedes añadir configuraciones adicionales
}
Anotación @RolesAllowed
@RolesAllowed es una anotación del estándar JSR-250, que es muy parecida a @Secured. También comprueba los roles del usuario, pero es más "oficial" dentro del contexto de los estándares Java.
Ejemplo de uso:
import javax.annotation.security.RolesAllowed;
import org.springframework.stereotype.Service;
@Service
public class AnotherService {
@RolesAllowed("ROLE_MANAGER")
public String managerOnly() {
return "¡Disponible solo para managers!";
}
}
Activación de @RolesAllowed. Para que esta anotación funcione, tienes que activarla en la configuración de seguridad:
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
@Configuration
@EnableGlobalMethodSecurity(jsr250Enabled = true) // Activamos el soporte para @RolesAllowed
public class SecurityConfig {
}
@Secured y
@RolesAllowed a la vez, debes activar ambos parámetros (
securedEnabled = true y
jsr250Enabled = true).
Anotación @PreAuthorize
Esta es una anotación más potente, que permite usar SpEL (Spring Expression Language) para definir las condiciones de acceso. Puedes comprobar no solo roles, sino también valores de atributos del objeto que el método recibe como parámetro.
Por ejemplo, puedes escribir una condición tipo "el acceso está permitido solo a usuarios cuyo email termine en @example.com".
Ejemplo:
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service;
@Service
public class EmailService {
@PreAuthorize("hasRole('ROLE_USER')")
public void generalAccess() {
System.out.println("Este método está disponible solo para usuarios con el rol ROLE_USER.");
}
@PreAuthorize("#email.endsWith('@example.com')")
public void sendEmail(String email) {
System.out.println("Enviando email a la dirección: " + email);
}
}
hasRole, y en el segundo se usa una expresión que depende del parámetro del método.
Activación de @PreAuthorize
Para activar el soporte de esta anotación, añade en la configuración:
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true) // Activamos soporte para @PreAuthorize y @PostAuthorize
public class SecurityConfig {
}
Anotación @PostAuthorize
¿En qué se diferencia de @PreAuthorize? Mientras que @PreAuthorize comprueba el acceso antes de la llamada al método, @PostAuthorize lo hace después de ejecutar el método. Esto es útil cuando quieres verificar el resultado antes de devolverlo al llamador.
Ejemplo:
import org.springframework.security.access.prepost.PostAuthorize;
import org.springframework.stereotype.Service;
@Service
public class DocumentService {
@PostAuthorize("returnObject.owner == authentication.name")
public Document getDocumentById(Long id) {
Document document = findDocumentById(id); // Lógica de búsqueda del documento
return document; // Comprobación de que el usuario actual es el propietario del documento
}
}
En este ejemplo, el permiso para devolver el documento se comprueba después de que el método lo haya encontrado. Si el usuario actual no es el dueño del documento, Spring Security lanzará una excepción.
Role-based Access Control (RBAC)
Ahora que hemos visto las anotaciones, hablemos de cómo todo esto se relaciona con el concepto de control de acceso basado en roles.
En un sistema RBAC, los usuarios reciben roles concretos (por ejemplo, ROLE_USER, ROLE_ADMIN), y esos roles tienen acceso a determinados recursos. Esto permite gestionar los permisos de forma efectiva, estableciendo reglas de manera centralizada.
Práctica: Configurar seguridad con anotaciones
Vamos a crear un servicio donde los usuarios puedan:
- Ver la lista de sus tareas (disponible solo para usuarios con el rol
ROLE_USER). - Eliminar una tarea (disponible solo para administradores).
public class Task {
private Long id;
private String name;
private String owner;
// Getters y Setters
}
Servicio:
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
@Service
public class TaskService {
private List<Task> tasks = new ArrayList<>();
public TaskService() {
// Añadimos algunas tareas como ejemplo
tasks.add(new Task(1L, "Comprar leche", "user1"));
tasks.add(new Task(2L, "Hacer la tarea", "user2"));
}
@PreAuthorize("hasRole('ROLE_USER')")
public List<Task> getUserTasks(String owner) {
// Lógica de filtrado de tareas por propietario
return tasks.stream()
.filter(task -> task.getOwner().equals(owner))
.toList();
}
@Secured("ROLE_ADMIN")
public void deleteTask(Long id) {
// Lógica para eliminar la tarea
tasks.removeIf(task -> task.getId().equals(id));
}
}
Controlador:
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/tasks")
public class TaskController {
private final TaskService taskService;
public TaskController(TaskService taskService) {
this.taskService = taskService;
}
@GetMapping
public List<Task> getTasks(@RequestParam String owner) {
return taskService.getUserTasks(owner);
}
@DeleteMapping("/{id}")
public void deleteTask(@PathVariable Long id) {
taskService.deleteTask(id);
}
}
Ahora hemos configurado la autorización tanto por roles como por expresiones.
Errores típicos
- Olvidar activar las anotaciones: si no habilitas
@EnableGlobalMethodSecurity, ninguna anotación funcionará. - Falta de roles en el usuario: si el usuario no tiene roles asignados, todos los métodos protegidos estarán inaccesibles para él.
- Mezclar diferentes anotaciones: si usas
@PreAuthorizey@Secured, no olvides activar el soporte para ambos parámetros.
Ahora sabes cómo configurar la seguridad con anotaciones. Es una herramienta potente que simplifica la gestión de accesos en las aplicaciones.
GO TO FULL VERSION