В сервисном методе нужно проверить или пользователь обладает необходимыми разрешениями для выполнения бизнес логики
Вот примерный код который сейчас это делает:
void processProduct() {
if (!SecurityUtils.hasRole("PERMISSION_UPDATE_ANY_PRODUCT")) {
if (SecurityUtils.hasRole("PERMISSION_UPDATE_OWN_PRODUCT")) {
if (!product.getAuthor().equals(user)) {
throw new AccessDeniedException("Access denied, you don't have permission to update other's products");
}
} else {
throw new AccessDeniedException("Access denied, you don't have permissions to update products");
}
}
if (product.getParent() != null) {
Long parentProductId = product.getParent().getId();
Product siblingProduct = productDao.findChildProductByName(parentProductId, name);
if (siblingProduct != null) {
if (!siblingProduct.equals(product)) {
throw new ProductAlreadyExistsException("Parent product already contains a child product with a given name");
}
}
}
.....
doSomeRealBusinessLogic();
}
Здесь проверяется или у пользователя есть разрешение — PERMISSION_UPDATE_ANY_PRODUCT и если есть то все ок, если нету то проверяется или есть разрешение PERMISSION_UPDATE_OWN_PRODUCT и если есть то принадлежит ли Продукт пользователю который пытается совершить над ним действие.
Затем вторая проверка проверяет или еще не содержится такого продукта с таким же именем у отцовского продукта(продуты в системе композитные)
Как можно отрефакторить этот метод и убрать проверки из сервисного метода во что то извне ?
Здравствуйте, brunoid, Вы писали:
B>Как можно отрефакторить этот метод и убрать проверки из сервисного метода во что то извне?
Можно просто вынести первый блок в Security слой, который оборачивает ваш Transaction Script.
Ещё можно взять security фреймверк (Spring Security, Apache Shiro) и переписать всю безопасность на него. hasRole смело уезжает в аннотации. Для проверки product.getAuthor().equals(user) тоже есть какие-то фичи. Либо Access Control List, либо как-то ещё. Надо вникать уже в конкретный фреймверк.
Здравствуйте, Blazkowicz, Вы писали:
B>Можно просто вынести первый блок в Security слой, который оборачивает ваш Transaction Script. B>Ещё можно взять security фреймверк (Spring Security, Apache Shiro) и переписать всю безопасность на него. hasRole смело уезжает в аннотации.
Spring Security плотно используется в проекте + еще Spring Social и раньше еще был Spring OAuth2 но сейчас переписали на JWT.
Методы контроллеров засекьюрены аннотациями, например:
@PreAuthorize("hasAnyRole('PERMISSION_UPDATE_OWN_PRODUCT', 'PERMISSION_UPDATE_ANY_PRODUCT')")
@RequestMapping(value = "/{productId}/update", method = RequestMethod.POST)
public DecisionResponse updateProduct(@PathVariable @NotNull @DecimalMin("0") Long productId, @Valid @RequestBody UpdateProductRequest productRequest) {
Product product = productService.updateProduct(productId, productRequest.getName(), productRequest.getDescription());
return new ProductResponse(product);
}
здесь все вроде как ок.. проблемы начинаются в сервисном методе productService.updateProduct в котором в зависимости от конкретного пермишена PERMISSION_UPDATE_OWN_PRODUCT или PERMISSION_UPDATE_ANY_PRODUCT происходят разные валидации(как я описывал в своем первом посте).
Можно конечно разнести логику по различным методам.. например updateOwnProduct или updateAnyProduct но мне кажется это тоже не лучшее решение.
B>Для проверки product.getAuthor().equals(user) тоже есть какие-то фичи. Либо Access Control List, либо как-то ещё. Надо вникать уже в конкретный фреймверк.
вот хотелось бы узнать какие фичи есть для этого и как их увязать с проблемой описанной выше.
Spring Security поддерживает Spring EL, поэтому в аннотациях можно писать сложные выражения:
@PreAuthorize("hasRole('PERMISSION_UPDATE_ANY_PRODUCT') or (hasRole('PERMISSION_UPDATE_OWN_PRODUCT') and #product.author==authentication.name)")
void processProduct() {
...
}
Здравствуйте, brunoid, Вы писали:
B>Можно конечно разнести логику по различным методам.. например updateOwnProduct или updateAnyProduct но мне кажется это тоже не лучшее решение.
А по-моему это отличное решение, которое на много лучше читается. Только не updateAnyProduct, а просто updateProduct будет правильнее.
Здравствуйте, brunoid, Вы писали:
B>А если обьект продукта достается только внутри метода processProduct и не известен заранее ? как в таком случае поступить ?
Зарефакторить. Сигнатура processProduct(Product) вообще, как бы, должна подразумевать наличие продукта. Иначе метод назван как-то криво.
Здравствуйте, brunoid, Вы писали:
B>спасибо. А если обьект продукта достается только внутри метода processProduct и не известен заранее ? как в таком случае поступить ?
Оставить как есть или поизвращаться, например, так:
Здравствуйте, Blazkowicz, Вы писали:
B>Здравствуйте, brunoid, Вы писали:
B>>А если обьект продукта достается только внутри метода processProduct и не известен заранее ? как в таком случае поступить ? B>Зарефакторить. Сигнатура processProduct(Product) вообще, как бы, должна подразумевать наличие продукта. Иначе метод назван как-то криво.
ну да, понял. Вобщем идея годная.. посмотрю как мне ее таким способом удастся реализовать.
Здравствуйте, brunoid, Вы писали:
B>А если обьект продукта достается только внутри метода processProduct и не известен заранее? как в таком случае поступить?
В сложных проектах роли (права доступа) обычно не выступают сами по себе, а при их выдаче пользователю определяется также круг объектов, по отношению к которым он может это право реализовать.
В таких случаях я предпочитаю не выносить в аннотации логику авторизации доступа, а оставлять её в первых строках конкретного бизнес-метода. Но иногда даже это не помогает, как, например, в случае чтения некоторых объектов по фильтру, зависящему от реальных прав. Тогда в первых строках ведётся не только проверка наличия нужных прав, но и подготовка дескрипторов фильтрации, в дальнейшем задействующихся при построении реальных запросов и/или точечных проверок по одиночным объектам.
В реальности у нас есть отделение низкоуровневого сервисного уровня (где "по-простому без интеллектуализма" совершаются всякие SQL-транзакции, вызовы веб-сервисов и т.п. вещи) от высокоуровневого фасадногоя слоя бизнес-логики (на котором извлекается контекстно-зависимая информация, делается авторизация, оркестрация, аудит и прочие наносные вещи). Т.е. определённое смысловое разделение присутствует.