Как бороться с иерархическими ролями/разрешениями, используя Apache Shiro?

Я пытаюсь использовать инфраструктуру Apache Shiro для защиты своего веб-приложения (пользовательский интерфейс основан на Vaadin 6). Просмотрел все примеры на сайте Shiro, а также работал в Интернете в течение нескольких часов, но я не могу найти чистый способ справиться со следующими требованиями.

Предполагая, что приложение является своего рода инструментом управления проектами, где пользователи создают действия, которые принадлежат определенным отделам иерархии компаний. Каждый пользователь может работать в нескольких отделах и имеет разные роли безопасности в каждом отделе. Пример:

Department A       - User is 'Manager' here
Department B
  Department C     - User is 'Admin' here
    Department D

Пользователь - "Менеджер" в отделе A Пользователь - "Администратор" в отделе C Пользователь также должен наследовать роль "Admin" для отдела D (который является предком Департамента C).

Итак, проверка основного разрешения (при условии, что я хочу просмотреть активность, принадлежащую некоторому отделу):

  • Проверить, имеет ли пользователь активности активность просмотр, принадлежит ли пользователь отдела, имеет роль в;
  • Убедитесь, что пользователь получил разрешение, основанное на его роли в этом отделе.

Я в настоящее время застрял в понимании того, как реализовать не только просто "роль системы", но и "роль в этом конкретном отделе".

Как я могу преобразовать вышеприведенный пример в строку разрешений типа "activity: view: 123"? И как я могу проверить разрешение в своей бизнес-логике?

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

Ответ 1

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

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

  • activity: edit
  • Активность: вид

И мне также нужно убедиться, что разрешения не являются системными, но зависит от моей роли в определенном отделе. То, что я сделал, явно добавило "зависимое от отдела" разрешение для пользователя в моем Царстве. Пример (см. Иерархию в сообщении):

  • DEP_A: активность: вид
  • DEP_C: активность: вид
  • DEP_C: активность: изменить

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

Активность A принадлежит отделу D, я хочу "просмотреть" его. Разрешения для проверки будут следующими:

  • DEP_D: активность: вид
  • DEP_C: активность: вид
  • DEP_B: активность: вид

Если я являюсь администратором отдела C, я бы получил разрешение "DEP_C: activity: view" и, следовательно, проверка будет передана. Это позволяет реализовать наследование прав в иерархии структуры компании.

Вот код snipplet из моего класса сервиса, ответственного за проверки разрешений:

   @Override
   public void checkIfOperationPermitted( SecurityOperation operation, 
      Object object )
   {
      final Subject currentUser = SecurityUtils.getSubject();

      if(currentUser.isPermitted(
         SecurityOperation.SYSTEM_ADMIN.getPermissionString()) ||
         currentUser.hasRole( "admin" ))
      {
         // no need to check anything else,
         // admin is system wide role.
         return;
      }

      if(object instanceof Activity)
      {
         // Activity permissions fully depends on organization and
         // product hierarchies. PermissionResolver is just a class
         // which generates list of permission strings based on 
         // department activity is belonging to.
         Activity a = (Activity) object;
         List<String> permissionsToCheck = 
            permissionResolver.resolveHierarchicalPermissions(operation, a);
         boolean permitted = false;
         for(String permission: permissionsToCheck)
         {
            if(currentUser.isPermitted( permission ))
            {
               permitted = true;
               break;
            }
         }
         if(!permitted)
         {
            throw new UnauthorizedException( "Access denied" );
         }
      }
      else
      {
         // Check for system wide permissions
         currentUser.checkPermission( operation.getPermissionString() );
      }
   }

Другой способ, о котором я думал, - добавить все такие разрешения для пользователя в моем Realm, но отклонил это, поскольку иерархия компаний может содержать N уровней, что значительно увеличивает дублирование в списке разрешений для конкретного пользователя (использование памяти).