как реализовать безопасность на уровне строк в весенних данных jpa с помощью фильтра гибернации или другими способами?

Одной из очень важных проблем информационных программ является наличие пользователей с разными ролями, обязанностями и уровнями доступа. Например, подумайте об организации со структурой (иерархией), как показано ниже:

[Organization Role ]     [Organization ID]
 CEO                        org01
   Financial Assistant      org0101
           personnel 1

   Software Assistant       org0102
           personnel 2

   Commercial Assistant     org0103
           personnel 3

Представьте, что в этой организации есть система, которая управляет информацией о персонале. Правило отображения кадровой информации в данной системе заключается в том, что каждый пользователь может видеть кадровую информацию организаций, к которым он имеет доступ; Например, «user1» имеет доступ к уровням «Финансовый помощник» и «Коммерческий помощник», поэтому он может видеть информацию только о «персонале 1» и «сотруднике 3». Точно так же «пользователь 2» имеет доступ только к уровню «Коммерческий помощник», поэтому он может видеть только информацию о «сотруднике 3». Поэтому каждый из пользователей в этой системе имеет определенный уровень доступа. Теперь учтите, что в этой системе каждый пользователь видит только ту персональную информацию, к которой у него есть доступ после входа в систему. При этом структура таблицы в этой системе выглядит следующим образом:

[Organization]
id
code
name

[Employee]
id
first_name
last_name
organization_id

[User]
id
user_name
password

[UserOrganization]
user_id
organization_id

приведенного ниже запроса будет достаточно, чтобы получить правильные результаты информации о персонале для каждого пользователя:

select *

from employee e 

where e.organization_id in

(select uo.organization_id

 from user_organization uo

 where uo.user_id=:authenticatedUserId)

как мы видим, следующее условие определяет логику доступа для отображения правильных данных:

e.organization_id in

(select uo.organization_id

 from user_organization uo

 where uo.user_id=:authenticatedUserId)

Этот тип уровня доступа также известен как «Безопасность на уровне строк» ​​(RLS). С другой стороны, соответствующий класс репозитория, вероятно, имеет несколько методов, отвечающих за чтение данных, и все они должны удовлетворять условиям надлежащего уровня доступа. В этом случае условие уровня доступа будет повторяться в некоторых местах (методах). Кажется, что использование «спящего фильтра» было бы подходящим решением этой проблемы. Единственное, что нужно, — это фильтр, который получает идентификатор аутентифицированного пользователя и выполняет команду «enablefilter» перед каждым методом чтения.

@Filters( {
  @Filter(name=“EmployeeAuthorize", condition="(organization_id in (select uo.organization_id from user_organization uo where uo.user_id=:authenticatedUserId) )  ")
} )

Теперь вопрос в том, правильно ли предложенное решение? Если да, то как этот метод можно использовать в весенних данных? PS: Учитывая, что мы не хотим зависеть от баз данных, реализация на стороне базы данных не может быть решением-кандидатом, по этой причине мы обязаны реализовать ее на стороне приложения (уровень).


person ali akbar azizkhani    schedule 15.10.2017    source источник
comment
Представьте, что количество узлов организации составляет 100 000, и вы хотите получить доступ ко всей организации для пользователя-администратора? как можно реализовать с помощью ACL? когда в организации будет создан новый узел, я добавлю доступ для всех пользователей. у него не очень хорошая производительность, представьте, что у нас есть 10 000 пользователей и сколько записей будет сохранено в таблице доступа?   -  person ali akbar azizkhani    schedule 15.10.2017


Ответы (2)


Али, это интересный сценарий.

Здесь вам нужно ответить на два вопроса.

Первый вопрос: при предоставлении данных система будет просто фильтровать их или вы пойдете дальше? Например, если вы предоставляете такую ​​операцию, как users/{id}, вам необходимо проверить авторизацию и убедиться, что у пользователя есть доступ к этой операции. Если вы просто выставите операцию типа /users, тогда все, что вам нужно, это фильтрация, потому что вы просто выставите пользователей, которых текущий пользователь имеет право видеть. Это различие во многом определит реализацию.


Второй вопрос: сколько ручной работы вы можете выполнять?

С одной стороны, вы можете адаптировать данные к тому, что нужно фреймворку, и стараться максимально полагаться на встроенную функциональность (выражения безопасности, ACL). Или, с другой стороны, вы могли бы адаптировать код к структуре ваших данных и больше делать что-то вручную.

Это два фактора, на которых я бы сосредоточился в первую очередь, прежде всего, потому что реализация будет выглядеть совершенно по-другому на основе этих четырех решений.


Наконец, чтобы ответить на ваш вопрос "можно ли масштабировать ACL" - два быстрых примечания. Один - нужно тестить. Да, ACL можно масштабировать, но можно ли масштабировать его до 10 000 или 100 000 000 000 000 000 000 000 000 000 000 000 000 000 000 или 100 000 000 000 000 000 000 000 000, это вопрос, на который нельзя ответить конкретно, без тестирования.

И во-вторых, когда вы проводите тестирование, продумайте реалистичные сценарии. Конечно, важно понимать пределы вашего решения. Но помимо этого, если вы думаете, что в вашей системе будет 1 миллион сущностей — отлично. Но если не будет - не ставьте это целью.

Надеюсь, это поможет.

person Eugen    schedule 16.10.2017
comment
Основываясь на том факте, что один поиск производит соединение по 4 таблицам, я сомневаюсь, что реализация по умолчанию будет хорошо масштабироваться в этом конкретном случае (хотя я также могу ошибаться). Если хранение данных в третьей нормализованной форме не требуется, вы также можете использовать подход MongoDB. , который создает только одну запись для каждого объекта домена и, следовательно, должен масштабироваться линейно. К сожалению, я не получил реального ответа от сообщества Spring Security, если они заинтересованы в интеграции ACL на основе MongoDB в свою кодовую базу. - person Roman Vottner; 16.10.2017
comment
Привет @RomanVottner - краткая заметка о MongoDB и ACL. В общем, то, что я видел, лучше всего работает в экосистеме Spring — это решение сообщества, которое в конечном итоге интегрируется и официально принимается. Если вы заинтересованы в том, чтобы поработать над этим, лучшим способом было бы развернуть это. Есть шанс, что в конечном итоге он станет официальным, но даже если это не так, сообщество все равно может его использовать. - person Eugen; 16.10.2017
comment
спасибо, да, я хочу разрешить пользователю сохранять и загружать. в сохранении я не могу справиться с этим без acl или выражения. но в объекте загрузки фильтр гибернации в порядке. Но при использовании ACl в этом сценарии моя проблема заключается в изменении доступа user_organization и добавлении и удалении ACL_ENTRY - person ali akbar azizkhani; 16.10.2017
comment
когда количество организаций составляет 100 000, а у меня 200 000 пользователей, что такое количество ACL_ENTRY? 100 000 * 200 000? как я могу справиться с этим? - person ali akbar azizkhani; 16.10.2017
comment
@aliakbarazizkhani зависит от того, сколько фактических правил вы определили для каждого ACL (~= организация). Пользователи будут SID в ваших настройках ACL, в то время как организации будут ObjectIdentity, на которые указывает ACL, а AccessControlEntry будет определять фактическое разрешение для конкретного пользователя в данном объекте домена. Таким образом, если вы не определяете правила для каждого пользователя отдельно, количество должно быть намного меньше. Хотя таблица соединений, вероятно, накопит еще много данных до того, как сработает фильтрация (но я не эксперт по SQL). Обратите внимание, что разрешения наследуются - person Roman Vottner; 16.10.2017
comment
@Eugen Я вижу код ACL (JdbcMutableAclService.java), при обновлении ACL все записи удаляются и создаются заново. когда у меня 200 000 записей, что происходит, когда я хочу добавить новую запись? Это нормально? - person ali akbar azizkhani; 19.10.2017

С Spring вы можете использовать следующие вещи:

1) Вы можете использовать Расширение SpEL EvaluationContext, которое делает свойства и выражения безопасности доступными в выражениях SpEL в аннотациях @Query. Это позволяет получить только те бизнес-объекты, которые относятся к текущему пользователю:

interface SecureBusinessObjectRepository extends Repository<BusinessObject, Long> {

    @Query("select o from BusinessObject o where o.owner.emailAddress like ?#{hasRole('ROLE_ADMIN') ? '%' : principal.emailAddress}")
    List<BusinessObject> findBusinessObjectsForCurrentUser();
}

2) Вы можете ссылаться в Beans и используйте переменные пути в Web Security Expressions. Это позволяет предоставлять доступ только к тем объектам, которые разрешены текущему пользователю:

@Service
public class UserService {
    public boolean checkAccess(Authentication authentication, int id) {
        // ...
    }
}

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    // ...

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
            .antMatchers("/businessObjects/{id}/**").access("@userService.checkAccess(authentication, #id)")
            // ...
    }
}

Проверьте мой демонстрационный проект для получения дополнительной информации. В этом примере Пользователи могут получить доступ к Комнатам, если они принадлежат к категориям, связанным с этими Пользователями. Администратор имеет доступ ко всем комнатам.

person Cepr0    schedule 15.10.2017
comment
когда ваша категория составляет 1 000 000, ваше решение работает? - person ali akbar azizkhani; 15.10.2017