Допустим, у меня есть минимальный контроллер RESTful, подобный следующему, кстати, с использованием Java 8 и Spring Boot 2,...
@RestController
class MyController {
@Autowired
private PostService service;
@GetMapping
public Post get() {
return service.getLatest();
}
}
Я успешно защитил этот маршрут с помощью модуля Spring Security. Теперь я хочу разрешить доступ к этому ресурсу только владельцу ресурса. Под владельцем ресурса я подразумеваю создателя или говоря просто:
Post myPost = new Post();
...
myPost.getCreator().equals(currentUser); // Should be true when access is granted
Я много нашел о доступе на основе ролей, но почти ничего о проверке владения... Конечно, я мог бы поместить оператор if внутри контроллера и выдать исключение, но я намеревался использовать что-то вроде Spring Управление доступом на основе выражений.
Любые другие идеи? Есть ли у кого-нибудь хорошая идея или пример проверки права собственности на ресурс?




В подходе, основанном на ролях, роли предопределены независимо от ответа/ресурса. Но в вашем случае нам нужно сначала получить ресурс, чтобы проверить, является ли пользователь владельцем или нет. Мы не можем решить заранее.
Допустим, если у пользователя есть ROLE_ADMIN, он будет администратором всех ресурсов.
В вашем случае право собственности зависит исключительно от ресурсов. Советую идти с ручной if проверкой. В случае, если объект Post тяжелый, вы можете вести отдельную таблицу всего с двумя столбцами resource_id и owner. затем, если владелец совпадает, вы можете получить ресурс позже, в противном случае вы можете отправить пользователю 403 запрещенный ответ.
Да, как я уже упоминал, я пробовал это, но для многих маршрутов и разных ресурсов вам нужно написать много шаблонного кода...
Для простой операции получения вы можете просто вернуть сообщение, связанное с вашим текущим зарегистрированным пользователем.
@GetMapping
public Post getPost(Authentication authentication) {
return service.getPostByUser(authentication.getName());
}
Для обновления существующего сообщения вы можете проверить в PreAuthorize, является ли создатель вошедшим в систему пользователем. authentication.getName() возвращает электронное письмо в моем примере
@PutMapping
@PreAuthorize("#post.getCreator() == authentication.getName()")
public void update(@RequestBody Post post, Authentication authentication) {
service.updatePost(post);
}
Базовый пример использования @Component
@Autowired
private CreatorCheck creatorCheck;
@PutMapping
@PreAuthorize("@creatorChecker.check(#post,authentication)")
public void update(@RequestBody Post post, Authentication authentication) {
service.updatePost(post);
}
И компонент.. Может быть расширен, чтобы получить исходный пост и проверить этого создателя..
@Component
public class CreatorCheck {
public boolean check(Post post, Authentication authentication) {
return post.getCreator().equals(authentication.getName());
}
}
Для получения более подробного руководства ознакомьтесь с этой ссылкой руководство, найденной 0x1C1B.
Но это означает, что клиент устанавливает создателя сообщения внутри запроса, верно? Как я могу предотвратить, что он манипулирует создателем? Скажем, используя свой собственный идентификатор для предоставления доступа... Или для запроса DELETE нет DTO/полезной нагрузки в качестве аргумента...
Вы можете написать другой общедоступный класс @Component CreatorChecker для обработки проверок. @PreAuthorize("@creatorChecker.check(#post,authentication)") В этом компоненте вы извлекаете сообщение из БД и проверяете создателя service.getPostId().getCreator() == authentication.getName() вот так
Где используется этот компонент? Как часть цепочки фильтров? Это общий подход, я ничего подобного не нашел... Но, похоже, интересное решение
Он будет оцениваться так же, как некоторые вещи по умолчанию, такие как hasRole('FOO'), поскольку это просто другое выражение, которое необходимо оценить. Ссылка на документы Spring
Это мое любимое решение... Я нашел базовый пример об использовании внешних компонентов для авторизации. Не могли бы вы обновить свой ответ, чтобы я мог его принять?
Добавлен базовый пример для вашего конкретного варианта использования и ссылка на найденный вами учебник.
Здесь — небольшой гайд, который помог мне решить эту проблему. Если вы используете репозиторий CRUD:
@Override
@PreAuthorize("#entity.id == authentication.principal.id")
<S extends User> S save(S entity);
Для владельца ресурса и администратора:
@PreAuthorize("#entity.id == authentication.principal.id || hasAuthority('ADMIN')")
поэтому просто переопределите требуемый метод в дочернем репозитории CRUD. Это работает и для @RepositoryRestResource
Я думать (но я не уверен), "доступ на основе владельца" называется «Дискретный контроль доступа», потому что владелец всегда имеет доступ, и владелец решает, кто еще может получить доступ к ресурсу. Сравните это с «Обязательный контроль доступа», где системные политики определяют, кто имеет доступ к ресурсу. В системе обязательного контроля доступа владелец/создатель ресурса может не иметь доступа к ресурсу.