как реализовать безопасность уровня строки в spring data jpa с помощью hibernate filter или другими способами?

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

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

   Software Assistant       org0102
           personnel 2

   Commercial Assistant     org0103
           personnel 3

представьте, что эта организация имеет систему, которая управляет информацией персонала. Правило отображения информации персонала в этой системе заключается в том, что каждый пользователь может видеть информацию персонала организаций, которые у него есть доступ к; например, "user1" имеет доступ к уровням "финансовый помощник" и "коммерческий помощник", поэтому он может видеть только информацию о "персонале 1" и "персонале 3". Аналогично, "user2" имеет доступ только к уровню "коммерческий помощник", поэтому он может видеть только информацию о "персонале 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: учитывая, что мы не хотим зависеть от баз данных, реализация на стороне базы данных не может быть решением-кандидатом, по этой причине мы обязаны реализовать его на стороне приложения (уровень).

2 ответов


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

здесь два вопроса вы должны ответить здесь.

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


второй вопрос - сколько ручного труда ты с делаешь?

с одной стороны, вы можете адаптировать данные к тому, что нужно фреймворку - и попытаться как можно больше полагаться на встроенную функциональность (выражения безопасности, СПИСОК КОНТРОЛЯ ДОСТУПА.) Или, с другой стороны, вы можете адаптировать код к структуре ваших данных - и делать вещи более вручную.

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


наконец, чтобы ответить на ваш вопрос "can ACL scale" - две быстрые заметки. Первое-нужно проверить. Да, ACL могут данные, но это данные на 10К или 100к не вопрос, что может быть ответил конкретно, без проверки.

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

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


С 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) можно см. фасоль и использовать переменные пути в выражениях веб-безопасности. Это позволяет предоставить доступ только те объекты, которые разрешены текущему пользователю:

@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)")
            // ...
    }
}

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