Spring Security предоставляет мощный механизм для конфигурации безопасности через аннотации. Это делает наш код не только компактнее, но и более читаемым. Аннотации позволяют разграничивать доступ к ресурсам и методам прямо там, где они используются.
В этой лекции мы изучим четыре основные аннотации:
@Secured@RolesAllowed@PreAuthorize@PostAuthorize
Аннотация @Secured
Аннотация @Secured задаёт роли, которым разрешён доступ к методу или ресурсу. Она является простой и удобной, если вам нужен быстрый способ ограничить доступ на основе ролей.
@Secured проверяет, есть ли у пользователя определённая роль (например, ROLE_USER), чтобы он мог вызвать данный метод.
Пример:
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("Этот метод доступен только для администраторов!");
}
@Secured({"ROLE_USER", "ROLE_ADMIN"})
public void userOrAdminMethod() {
System.out.println("Этот метод доступен для пользователей и администраторов.");
}
}
@EnableGlobalMethodSecurity с параметром
securedEnabled = true.
Как активировать? Добавьте следующую настройку в класс конфигурации безопасности:
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true) // Включаем поддержку @Secured
public class SecurityConfig {
// Здесь можно добавлять дополнительные конфигурации
}
Аннотация @RolesAllowed
@RolesAllowed — это аннотация из стандарта JSR-250, которая очень похожа на @Secured. Она также проверяет роли пользователя, но является более "официальной" в контексте стандартов Java.
Пример использования:
import javax.annotation.security.RolesAllowed;
import org.springframework.stereotype.Service;
@Service
public class AnotherService {
@RolesAllowed("ROLE_MANAGER")
public String managerOnly() {
return "Доступно только для менеджеров!";
}
}
Включение @RolesAllowed. Чтобы эта аннотация работала, нужно активировать её в конфигурации безопасности:
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
@Configuration
@EnableGlobalMethodSecurity(jsr250Enabled = true) // Включаем поддержку @RolesAllowed
public class SecurityConfig {
}
@Secured и
@RolesAllowed одновременно, то активировать нужно оба параметра (
securedEnabled = true и
jsr250Enabled = true).
Аннотация @PreAuthorize
Это уже более мощная аннотация, которая позволяет использовать SpEL (Spring Expression Language) для определения условий доступа. Можно проверять не только роли, но и значения атрибутов объекта, который метод получает на вход.
Например, вы можете написать условие вроде "доступ разрешён только пользователям, у которых email заканчивается на @example.com".
Пример:
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("Этот метод доступен только для пользователей с ролью ROLE_USER.");
}
@PreAuthorize("#email.endsWith('@example.com')")
public void sendEmail(String email) {
System.out.println("Отправка email на адрес: " + email);
}
}
hasRole, а во втором используется выражение, зависящее от параметра метода.
Включение @PreAuthorize
Чтобы активировать поддержку этой аннотации, добавьте в конфигурацию:
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true) // Включаем поддержку @PreAuthorize и @PostAuthorize
public class SecurityConfig {
}
Аннотация @PostAuthorize
Чем она отличается от @PreAuthorize? Если @PreAuthorize проверяет доступ до вызова метода, то @PostAuthorize делает это после выполнения метода. Это может быть полезно, когда вы хотите проверить результат выполнения метода перед тем, как вернуть его вызвавшей стороне.
Пример:
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); // Логика поиска документа
return document; // Проверка, что текущий пользователь является владельцем документа
}
}
В этом примере разрешение возвращать документ проверяется уже после того, как метод его нашёл. Если текущий пользователь не является владельцем документа, Spring Security выбросит исключение.
Role-based Access Control (RBAC)
Теперь, когда мы изучили аннотации, давайте обсудим, как это всё связано с концепцией управления доступом на основе ролей.
В системе RBAC пользователи получают определённые роли (например, ROLE_USER, ROLE_ADMIN), а уже роли имеют доступ к определённым ресурсам. Это позволяет эффективно управлять правами доступа, централизованно задавая правила.
Практика: Настройка безопасности с аннотациями
Давайте создадим сервис, где пользователи могут:
- Посмотреть список своих задач (доступно только пользователям с ролью
ROLE_USER). - Удалить задачу (доступно только администраторам).
public class Task {
private Long id;
private String name;
private String owner;
// Getters и Setters
}
Сервис:
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() {
// Добавим несколько задач для примера
tasks.add(new Task(1L, "Купить молоко", "user1"));
tasks.add(new Task(2L, "Сделать домашку", "user2"));
}
@PreAuthorize("hasRole('ROLE_USER')")
public List<Task> getUserTasks(String owner) {
// Логика фильтрации задач по владельцу
return tasks.stream()
.filter(task -> task.getOwner().equals(owner))
.toList();
}
@Secured("ROLE_ADMIN")
public void deleteTask(Long id) {
// Логика удаления задачи
tasks.removeIf(task -> task.getId().equals(id));
}
}
Контроллер:
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);
}
}
Теперь мы настроили авторизацию как через роли, так и через выражения.
Типичные ошибки
- Забыли активировать аннотации: если не включить
@EnableGlobalMethodSecurity, никакая аннотация работать не будет. - Отсутствие ролей у пользователя: если у пользователя не назначены роли, все защищённые методы будут для него недоступны.
- Смешивание разных аннотаций: если вы используете и
@PreAuthorize, и@Secured, не забывайте включить поддержку обоих параметров.
Теперь вы знаете, как конфигурировать безопасность с помощью аннотаций. Это мощный инструмент, который упрощает управление доступом в приложениях.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ