Java — эффективная авторизация на уровне экземпляра с учетом базы данных?

В приложении JPA у меня есть сценарий, в котором приложение должно

список всех учетных записей, с которых данный пользователь имеет право снимать

У меня есть сущность «Учетная запись» и таблица «многие ко многим», в которой перечислены авторизации, которые каждый пользователь имеет для каждой учетной записи — для реализации описанного выше сценария приложение в настоящее время просто объединяет две таблицы внутри, что довольно быстро.

Теперь я планировал добавить явный уровень авторизации (на основе apache shiro/spring security/other), чтобы изолировать логику, связанную с авторизацией, от остального кода, но...

В базе данных около 10 тысяч учетных записей, и «среднему» пользователю предоставляется «депозит» на всех из них, «просмотр» на одной половине и «снятие» только на нескольких.

Позволяет ли какой-либо фреймворк безопасности эффективно реализовать этот сценарий?

То есть: может ли кто-нибудь из них "украсить" JPA-запрос типа "выбрать a из учетной записи a" (или эквивалентный SQL) и таким образом получить список учетных записей без загрузки всех пользовательских грантов из базы данных и, во что бы то ни стало, без получения всех учетных записей?)


person giorgiga    schedule 28.05.2012    source источник
comment
... и у вас нет сущности пользователя, связанной с авторизацией, и вы можете просто ВЫБРАТЬ * ИЗ учетной записи, ГДЕ Account.userAuths.user = user?   -  person Visionary Software Solutions    schedule 28.05.2012
comment
Да - это то, что приложение делает прямо сейчас. Мне интересно, позволяет ли какая-либо структура аутентификации удалить это соединение, связанное с авторизацией, из кода.   -  person giorgiga    schedule 28.05.2012
comment
Может ли помочь запрос NamedNativeQuery, который возвращает объекты учетной записи, такие как «выберите учетную запись * из учетной записи учетной записи, user_auth uauth, где uauth.user_id =: login_userid»?   -  person Ahamed Mustafa M    schedule 28.05.2012
comment
Спасибо, но... нет :( Это снова код, связанный с авторизацией, и я бы не хотел, чтобы он был в том месте, где извлекаются учетные записи. Допустим, в какой-то момент мне нужно добавить пользователя root, которому разрешено выходить из все учетные записи, несмотря ни на что - с этим соединением мне придется обновить запрос (или - возможно, лучше - я должен убедиться, что в таблице USER_AUTH есть одна запись для root и каждой учетной записи)   -  person giorgiga    schedule 28.05.2012


Ответы (3)


Взгляните на Apache Shiro.

Это позволяет вам получить авторизацию пользователя один раз и кэшировать ее на время сеанса. Кроме того, если все пользователи могут ПРОСМОТР всех АККАУНТОВ, вам не нужно было бы явно определять это, что значительно уменьшило бы накладные расходы.

Если вашему решению требуются обработчики доступа в реальном времени, у Широ есть способ динамически сбросить разрешения во время выполнения.

Широ позволяет вам реализовать типичный RBAC и определить разрешения следующим образом:

domain:action:instance

Итак, в вашем случае разрешения для пользователя могут выглядеть так:

account:deposit:*  // deposit all accounts
account:view:1111
account:view:2222
account:view:3333 // view on these accounts
account:withdraw:5555
account:withdraw:6666  // withdraw on these accounts

В коде вы можете сделать что-то вроде этого:

if (SecurityUtils.getSubject().isPermitted("account:withdraw:"+account.getAccountNumber() ) {
  // handle withdraw
}

Широ также имеет разрешения на основе аннотаций для дополнительной абстракции.

ИЗМЕНИТЬ

Разрешения Широ — это конечный результат, а не то, с чего вы начинаете. Я использовал набор таблиц, представляющих сопоставления пользователя с ролью и роли с разрешением, а также другие сопоставления с экземпляром. После AuthN обычно это простой набор запросов, индексируемых User PK для создания структур данных, необходимых для предоставления разрешений.

person Jeff    schedule 18.09.2012
comment
Когда у вас есть учетная запись разрешений: представление: 1111,2222,3333 и т. д., как вы создаете запрос для выполнения запроса к базе данных? Анализируя часть уровня экземпляра разрешения? Представьте, что у вас есть тысячи идентификаторов в разрешении на просмотр. Это вообще не кажется очень эффективным. - person singe3; 18.11.2016

Я надеюсь, что это одна из возможностей реализовать ваше требование с помощью Spring-Security.

  1. Напишите собственный org.springframework.security.acls.Permission как ViewAccount,DepositToAccount,WithDrawFromAccount

  2. Напишите пользовательский org.springframework.security.access.PermissionEvaluator Override hasPermission(Authentication userAuthentication,Object accountObject,Object oneOfThePermission), чтобы проверить, есть ли у пользователя определенное разрешение на accountObject.

  3. Получите ссылку на JPA EntityManager в своем пользовательском оценщике и перепроверьте / проверьте в БД с помощью user_id, permission_id, account_id

  4. Если пользователь root, вы можете сразу же вернуть true для hasPermission без проверки с помощью БД.

  5. Аннотируйте свои обращения в службу поддержки с помощью @PreAuthorize("isAuthenticated() and hasPermission(#accountArgument, 'respectivePermission')")

См. ссылку для пользовательских реализаций Permission и PermissionEvaluator

person Ahamed Mustafa M    schedule 28.05.2012
comment
похоже, что hasPermission необходимо вызывать для каждого экземпляра учетной записи... Я боюсь, что это не может работать без извлечения всех строк из базы данных (т.е. загрузка 10 000 объектов, чтобы показать только несколько) - person giorgiga; 29.05.2012
comment
Обратитесь к этим ссылкам stackoverflow.com/questions/559480/ , jpasecurity.sourceforge.net - person Ahamed Mustafa M; 30.05.2012
comment
Эта вещь JPAsecurity кажется очень альфа-версией, но это также единственная применимая библиотека, которую я смог найти ... @Ahamed, если вы отредактируете свой ответ, сделав его ссылкой на jpasecurity (комментарии также будут оценены потенциальными читателями) я приму. - person giorgiga; 08.08.2012

Если вы используете EclipseLink, для этого есть несколько функций:

одна из них — аннотация @AdditionalCriteria, которая позволяет применять фильтр ко всем запросам для класса,

http://www.eclipse.org/eclipselink/documentation/2.4/jpa/extensions/a_additionalcriteria.htm#additionalcriteria

другой — поддержка EclipseLink для Oracle VPD (безопасность на уровне строк в базе данных),

http://wiki.eclipse.org/EclipseLink/Examples/JPA/Auditing

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

http://www.eclipse.org/eclipselink/api/2.4/org/eclipse/persistence/sessions/SessionEventAdapter.html#preExecuteQuery%28org.eclipse.persistence.sessions.SessionEvent%29

person James    schedule 19.09.2012