рефакторинг сервисного метода
От: brunoid  
Дата: 29.04.15 06:16
Оценка:
Проект построен на Spring 4 Framework.

В сервисном методе нужно проверить или пользователь обладает необходимыми разрешениями для выполнения бизнес логики

Вот примерный код который сейчас это делает:

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 и если есть то принадлежит ли Продукт пользователю который пытается совершить над ним действие.

Затем вторая проверка проверяет или еще не содержится такого продукта с таким же именем у отцовского продукта(продуты в системе композитные)

Как можно отрефакторить этот метод и убрать проверки из сервисного метода во что то извне ?
Отредактировано 29.04.2015 6:31 Blazkowicz . Предыдущая версия .
Re: рефакторинг сервисного метода
От: brunoid  
Дата: 29.04.15 06:18
Оценка:
Модераторы, поправьте пожалуйста разметку кода в моем сообщении, я не тем тегом его выделил ( Спасибо !
Re: рефакторинг сервисного метода
От: Blazkowicz Россия  
Дата: 29.04.15 06:37
Оценка:
Здравствуйте, brunoid, Вы писали:

B>Как можно отрефакторить этот метод и убрать проверки из сервисного метода во что то извне?


Можно просто вынести первый блок в Security слой, который оборачивает ваш Transaction Script.
Ещё можно взять security фреймверк (Spring Security, Apache Shiro) и переписать всю безопасность на него. hasRole смело уезжает в аннотации. Для проверки product.getAuthor().equals(user) тоже есть какие-то фичи. Либо Access Control List, либо как-то ещё. Надо вникать уже в конкретный фреймверк.
Re[2]: рефакторинг сервисного метода
От: brunoid  
Дата: 29.04.15 08:32
Оценка:
Здравствуйте, 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, либо как-то ещё. Надо вникать уже в конкретный фреймверк.


вот хотелось бы узнать какие фичи есть для этого и как их увязать с проблемой описанной выше.
Re: рефакторинг сервисного метода
От: Baudolino  
Дата: 29.04.15 09:18
Оценка:
Spring Security поддерживает Spring EL, поэтому в аннотациях можно писать сложные выражения:

@PreAuthorize("hasRole('PERMISSION_UPDATE_ANY_PRODUCT') or (hasRole('PERMISSION_UPDATE_OWN_PRODUCT') and #product.author==authentication.name)")
void processProduct() {
    ...
}
Re[2]: рефакторинг сервисного метода
От: brunoid  
Дата: 29.04.15 10:00
Оценка:
Здравствуйте, Baudolino, Вы писали:

B>Spring Security поддерживает Spring EL, поэтому в аннотациях можно писать сложные выражения:


B>
B>@PreAuthorize("hasRole('PERMISSION_UPDATE_ANY_PRODUCT') or (hasRole('PERMISSION_UPDATE_OWN_PRODUCT') and #product.author==authentication.name)")
B>void processProduct() {
B>    ...
B>}
B>



спасибо. А если обьект продукта достается только внутри метода processProduct и не известен заранее ? как в таком случае поступить ?
Re[3]: рефакторинг сервисного метода
От: Blazkowicz Россия  
Дата: 29.04.15 10:35
Оценка:
Здравствуйте, brunoid, Вы писали:

B>Можно конечно разнести логику по различным методам.. например updateOwnProduct или updateAnyProduct но мне кажется это тоже не лучшее решение.

А по-моему это отличное решение, которое на много лучше читается. Только не updateAnyProduct, а просто updateProduct будет правильнее.
Re[3]: рефакторинг сервисного метода
От: Blazkowicz Россия  
Дата: 29.04.15 11:29
Оценка:
Здравствуйте, brunoid, Вы писали:

B>А если обьект продукта достается только внутри метода processProduct и не известен заранее ? как в таком случае поступить ?

Зарефакторить. Сигнатура processProduct(Product) вообще, как бы, должна подразумевать наличие продукта. Иначе метод назван как-то криво.
Re[3]: рефакторинг сервисного метода
От: Baudolino  
Дата: 29.04.15 11:37
Оценка:
Здравствуйте, brunoid, Вы писали:

B>спасибо. А если обьект продукта достается только внутри метода processProduct и не известен заранее ? как в таком случае поступить ?


Оставить как есть или поизвращаться, например, так:

void processProduct() {
    Product product = ...
    productGuard.process(product, product -> {
         ...
    });
}


class ProductGuard {   

    @PreAuthorize(...)
    void process(Product product, Consumer<Product> consumer) { ... }

}
Re[4]: рефакторинг сервисного метода
От: brunoid  
Дата: 29.04.15 11:41
Оценка:
Здравствуйте, Blazkowicz, Вы писали:

B>Здравствуйте, brunoid, Вы писали:


B>>А если обьект продукта достается только внутри метода processProduct и не известен заранее ? как в таком случае поступить ?

B>Зарефакторить. Сигнатура processProduct(Product) вообще, как бы, должна подразумевать наличие продукта. Иначе метод назван как-то криво.

ну да, понял. Вобщем идея годная.. посмотрю как мне ее таким способом удастся реализовать.
Re[3]: рефакторинг сервисного метода
От: C0s Россия  
Дата: 29.04.15 14:45
Оценка:
Здравствуйте, brunoid, Вы писали:

B>А если обьект продукта достается только внутри метода processProduct и не известен заранее? как в таком случае поступить?


В сложных проектах роли (права доступа) обычно не выступают сами по себе, а при их выдаче пользователю определяется также круг объектов, по отношению к которым он может это право реализовать.
В таких случаях я предпочитаю не выносить в аннотации логику авторизации доступа, а оставлять её в первых строках конкретного бизнес-метода. Но иногда даже это не помогает, как, например, в случае чтения некоторых объектов по фильтру, зависящему от реальных прав. Тогда в первых строках ведётся не только проверка наличия нужных прав, но и подготовка дескрипторов фильтрации, в дальнейшем задействующихся при построении реальных запросов и/или точечных проверок по одиночным объектам.
В реальности у нас есть отделение низкоуровневого сервисного уровня (где "по-простому без интеллектуализма" совершаются всякие SQL-транзакции, вызовы веб-сервисов и т.п. вещи) от высокоуровневого фасадногоя слоя бизнес-логики (на котором извлекается контекстно-зависимая информация, делается авторизация, оркестрация, аудит и прочие наносные вещи). Т.е. определённое смысловое разделение присутствует.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.