как реализовать безопасность уровня строки в 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)")
// ...
}
}
Регистрация мой демо-проект для получения более подробной информации. В этом примере пользователи могут получить доступ к комнатам, если они принадлежат к категориям, связанным с этими пользователями. Администратор имеет доступ ко всем комнатам.