Разница между ролью и предоставленным полномочием в Spring Security

228

В Spring Security есть концепции и реализации, такие как GrantedAuthorityинтерфейс для получения полномочий на авторизацию / контроль доступа.

Я хотел бы, чтобы допустимые операции, такие как createSubUsers или deleteAccounts , которые я позволил бы администратору (с ролью ROLE_ADMIN).

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

Я вижу, hasRoleпотребляет GrantedAuthorityстроку? Я определенно делаю это неправильно в понимании. Что это концептуально в Spring Security?

Как сохранить роль пользователя отдельно от полномочий для этой роли?

Я также смотрю на org.springframework.security.core.userdetails.UserDetailsинтерфейс, который используется в DAO, на которую ссылается поставщик аутентификации, которая использует User(обратите внимание на последний GrantedAuthority):

public User(String username, 
            String password, 
            boolean enabled, 
            boolean accountNonExpired,
            boolean credentialsNonExpired, 
            boolean accountNonLocked, 
            Collection<? extends GrantedAuthority> authorities)

Или есть какой-то другой способ отличить два других? Или это не поддерживается, и мы должны сделать свои собственные?

Chinmay
источник

Ответы:

366

Думайте о предоставленном полномочии как о «разрешении» или «праве». Эти «разрешения» (обычно) выражаются в виде строк (с помощью getAuthority()метода). Эти строки позволяют вам определить разрешения и позволить вашим избирателям решать, предоставляют ли они доступ к чему-либо.

Вы можете предоставлять различные GrantedAuthoritys (разрешения) пользователям, помещая их в контекст безопасности. Обычно вы делаете это, реализуя свой собственный UserDetailsService, который возвращает реализацию UserDetails, которая возвращает необходимые GrantedAuthorities.

Роли (как они используются во многих примерах) - это просто «разрешения» с соглашением об именах, которое говорит, что роль - это GrantedAuthority, который начинается с префикса ROLE_. Там больше ничего нет. Роль - это просто предоставленное право - «разрешение» - «право». Вы видите много мест в весенней безопасности, где роль с ROLE_префиксом обрабатывается специально, например, в RoleVoter, где ROLE_префикс используется по умолчанию. Это позволяет вам предоставлять имена ролей без ROLE_префикса. До версии Spring security 4 эта особая обработка «ролей» не выполнялась очень последовательно, и полномочия и роли часто рассматривались одинаково (как выhasAuthority()hasRole()). В Spring Security 4 обработка ролей более последовательна, и код, который имеет дело с «ролями» (например RoleVoter, hasRoleвыражением и т. Д.), Всегда добавляет ROLE_префикс для вас. Значит так hasAuthority('ROLE_ADMIN')же, как и hasRole('ADMIN')потому, что ROLE_префикс добавляется автоматически. Дополнительную информацию см. В руководстве по миграции с 3 по 4 весенней безопасности .

Но все же: роль - это просто авторитет со специальным ROLE_префиксом. Так что в Spring безопасность 3 @PreAuthorize("hasRole('ROLE_XYZ')")такая же, как @PreAuthorize("hasAuthority('ROLE_XYZ')")и в Spring безопасность 4 @PreAuthorize("hasRole('XYZ')")такая же, как @PreAuthorize("hasAuthority('ROLE_XYZ')").

Относительно вашего варианта использования:

Пользователи имеют роли, а роли могут выполнять определенные операции.

Вы можете получить GrantedAuthoritiesроли, к которым принадлежит пользователь, и операции, которые может выполнять эта роль. Для GrantedAuthoritiesролей есть префикс, ROLE_а для операций - префикс OP_. Примером операции власти может быть OP_DELETE_ACCOUNT, OP_CREATE_USER, и OP_RUN_BATCH_JOBт.д. Роли могут быть ROLE_ADMIN, ROLE_USER, и ROLE_OWNERт.д.

Вы можете в конечном итоге реализовать свои сущности, GrantedAuthorityкак в этом (псевдокоде) примере:

@Entity
class Role implements GrantedAuthority {
    @Id
    private String id;

    @ManyToMany
    private final List<Operation> allowedOperations = new ArrayList<>();

    @Override
    public String getAuthority() {
        return id;
    }

    public Collection<GrantedAuthority> getAllowedOperations() {
        return allowedOperations;
    }
}

@Entity
class User {
    @Id
    private String id;

    @ManyToMany
    private final List<Role> roles = new ArrayList<>();

    public Collection<Role> getRoles() {
        return roles;
    }
}

@Entity
class Operation implements GrantedAuthority {
    @Id
    private String id;

    @Override
    public String getAuthority() {
        return id;
    }
}

Идентификатором ролей и операций, которые вы создаете в своей базе данных, будет представление GrantedAuthority, например ROLE_ADMIN, OP_DELETE_ACCOUNTи т. Д. Когда пользователь проходит аутентификацию, убедитесь, что все GrantedAuthorities всех его ролей и соответствующих операций возвращены из UserDetails.getAuthorities () метод.

Пример: админ роль с идентификатором ROLE_ADMINимеет операции OP_DELETE_ACCOUNT, OP_READ_ACCOUNT, OP_RUN_BATCH_JOBвозложенные на него. Роль пользователя с идентификатором ROLE_USERимеет операцию OP_READ_ACCOUNT.

Если админ журналы в результирующем контексте безопасности будет иметь GrantedAuthorities: ROLE_ADMIN, OP_DELETE_ACCOUNT, OP_READ_ACCOUNT,OP_RUN_BATCH_JOB

Если пользователь входит в систему , он будет иметь: ROLE_USER,OP_READ_ACCOUNT

UserDetailsService позаботится о том, чтобы собрать все роли и все операции этих ролей и сделать их доступными методом getAuthorities () в возвращенном экземпляре UserDetails.

Джеймс
источник
2
Спасибо! Я всюду искал, почему hasRole ('rolename') не работал в Spring 4 -> Их документация не была достаточно быстрой для просмотра. Просто быстрое «найти и заменить», и я снова на верном пути!
Jørgen Skår Fischer
12
Это отличный ответ. Чтобы пояснить это немного иначе, hasRole('xyz')в Spring Security 4 ожидается, что у вас будет префикс ROLE_, тогда как префикс hasAuthority('xyz')не ожидает и точно вычисляет, что передается. Я использовал это решение, но столкнулся с проблемами, hasRole('OP_MY_PERMISSION')поскольку ROLE_ был нужен префикс. Вместо этого я должен был использовать, hasAuthority('OP_MY_PERMISSION')так как у меня не было префикса.
randal4
@james вы можете посмотреть на этот вопрос stackoverflow.com/questions/41500031/…
saurabh kumar
1
В JSTL springframework.org/security/tags так <sec:authorize access="hasRole('ADMIN')">же, как<sec:authorize access="hasRole('ROLE_ADMIN')">
Alex78191
1
Да. OP_ это просто произвольный префикс. ROLE_ имеет особое значение в некоторых реализациях Spring.
Джеймс
9

AFAIK Предоставлено. Авторитет и роли одинаковы в весенней безопасности. Строка getAuthority () GrantedAuthority является ролью (согласно реализации по умолчанию SimpleGrantedAuthority).

Для вашего случая может быть, вы можете использовать иерархические роли

<bean id="roleVoter" class="org.springframework.security.access.vote.RoleHierarchyVoter">
    <constructor-arg ref="roleHierarchy" />
</bean>
<bean id="roleHierarchy"
        class="org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl">
    <property name="hierarchy">
        <value>
            ROLE_ADMIN > ROLE_createSubUsers
            ROLE_ADMIN > ROLE_deleteAccounts 
            ROLE_USER > ROLE_viewAccounts
        </value>
    </property>
</bean>

Не то, что вы ищете, но надеюсь, что это поможет

Изменить : Ответить на ваш комментарий

Роль как разрешение в весенней безопасности. Использование intercept-url с hasRole обеспечивает очень точный контроль того, какая операция разрешена для какой роли / разрешения.

То, как мы работаем в нашем приложении, мы определяем разрешение (т.е. роль) для каждой операции (или URL-адреса отдыха), например, для view_account, delete_account, add_account и т. Д. Затем мы создаем логические профили для каждого пользователя, такие как admin, guest_user, normal_user. Профили - это просто логическая группа разрешений, независимая от Spring-Security. Когда добавляется новый пользователь, ему присваивается профиль (имеющий все разрешенные разрешения). Теперь, когда пользователь пытается выполнить какое-либо действие, разрешение / роль для этого действия сравнивается с пользователем grantAuthorities.

Также в RoleVoter по умолчанию используется префикс ROLE_, поэтому любой авторитет, начинающийся с ROLE_, рассматривается как роль, вы можете изменить это поведение по умолчанию, используя собственный RolePrefix в роли избирателя и используя его в весенней безопасности.

кодировщик
источник
1
Спасибо за помощь. Иерархия выглядит как нечто другое. Теперь я думаю о том, как сделать это (если мне нужно использовать Spring Security), чтобы сохранить роль и полномочия в одном и том же списке и использовать предикат hasRole для выполнения проверок обоих. Думая об этом больше - это, вероятно, оставлено преднамеренно парнями в Spring Security? Есть ли возможность использовать URL-адрес перехвата, кроме проверки с помощью hasRole в полном списке ролей и привилегий / полномочий - или других приложений авторизации. Кроме того, что нужно префикс ROLE_? Это конвенция?
Чинмай
7

Еще один способ понять взаимосвязь между этими понятиями - это интерпретировать роль как контейнер полномочий.

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

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

С другой стороны, РОЛИ - это грубое представление набора разрешений. ROLE_READER будет иметь только права на чтение или просмотр, а ROLE_EDITOR будет иметь права на чтение и запись. Роли в основном используются для первого просмотра на окраине обработки запроса, такой как http. ... .antMatcher (...). hasRole (ROLE_MANAGER)

Полномочия, применяемые глубоко в потоке процесса запроса, позволяют более детально применять разрешение. Например, пользователь может иметь разрешение на чтение и запись для первого уровня ресурса, но только для чтения на подресурс. Наличие ROLE_READER ограничило бы его право редактировать ресурс первого уровня, так как ему нужно разрешение на запись для редактирования этого ресурса, но перехватчик @PreAuthorize может заблокировать его предварительное редактирование подресурса.

Джейк

softjake
источник
2

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

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

У меня может быть набор разрешений в приложении, например CREATE, READ, UPDATE, DELETE, которые затем связаны с ролью пользователя.

Или более конкретные разрешения, такие как READ_POST, READ_PUBLISHED_POST, CREATE_POST, PUBLISH_POST

Эти разрешения относительно статичны, но отношение ролей к ним может быть динамическим.

Пример -

@Autowired 
RolePermissionsRepository repository;

public void setup(){
  String roleName = "ROLE_ADMIN";
  List<String> permissions = new ArrayList<String>();
  permissions.add("CREATE");
  permissions.add("READ");
  permissions.add("UPDATE");
  permissions.add("DELETE");
  repository.save(new RolePermissions(roleName, permissions));
}

Вы можете создавать API для управления отношением этих разрешений к роли.

Я не хочу копировать / вставлять другой ответ, поэтому вот ссылка на более полное объяснение SO.
https://stackoverflow.com/a/60251931/1308685

Чтобы повторно использовать мою реализацию, я создал репо. Пожалуйста, не стесняйтесь вносить свой вклад!
https://github.com/savantly-net/spring-role-permissions

Джереми
источник