Рубрики
Без рубрики

Управление доступом на основе разрешений с помощью Apache Shiro

Узнайте, как реализовать детальное управление доступом на основе разрешений с помощью платформы безопасности Apache Shiro Java

Автор оригинала: Alessio Stalla.

1. введение

В этом уроке мы рассмотрим, как реализовать мелкозернистый контроль доступа на основе разрешений с помощью Apache Shiro Java security framework.

2. Настройка

Мы будем использовать ту же настройку, что и в нашем введении в Shiro, то есть мы добавим только модуль shiro — core в наши зависимости:


    org.apache.shiro
    shiro-core
    1.4.1

Кроме того, в целях тестирования мы будем использовать простой INIRealm, поместив следующий файл shiro.ini в корень пути к классу:

[users]
jane.admin = password, admin
john.editor = password2, editor
zoe.author = password3, author
 
[roles]
admin = *
editor = articles:*
author = articles:create, articles:edit

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

IniRealm iniRealm = new IniRealm("classpath:shiro.ini");
SecurityManager securityManager = new DefaultSecurityManager(iniRealm);
SecurityUtils.setSecurityManager(securityManager);

3. Роли и разрешения

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

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

Набор ролей обычно разрабатывается заранее и редко изменяется в соответствии с новыми бизнес-требованиями. Однако роли также могут определяться динамически — например, администратором.

С Shiro у нас есть несколько способов проверить, есть ли у пользователя определенная роль. Самый простой способ-использовать метод hasRole :

Subject subject = SecurityUtils.getSubject();
if (subject.hasRole("admin")) {       
    logger.info("Welcome Admin");              
}

3.1. Разрешения

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

Мы можем сделать лучше; вот почему мы сейчас введем понятие разрешений. Разрешения представляют то, что может сделать программное обеспечение, что мы можем разрешить или запретить, и не кто может это сделать. Например, “изменить профиль текущего пользователя”, “утвердить документ” или “создать новую статью”.

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

Subject subject = SecurityUtils.getSubject();
if (subject.isPermitted("articles:create")) {
    //Create a new article
}

Обратите внимание, что использование разрешений в Shiro совершенно необязательно.

3.2. Связывание разрешений с пользователями

У Shiro есть гибкая модель связывания разрешений с ролями или отдельными пользователями. Однако типичные области, включая простой INIrealm, который мы используем в этом руководстве, связывают разрешения только с ролями.

Таким образом, пользователь, идентифицированный Принципалом, имеет несколько ролей, и каждая роль имеет несколько Разрешений .

Например, мы можем видеть, что в вашем INI-файле пользователь zoe.автор имеет роль автор , и это дает им права статьи:создавать и статьи:редактировать :

[users]
zoe.author = password3, author
#Other users...

[roles]
author = articles:create, articles:edit
#Other roles...

Аналогично, другие типы областей (например, встроенная область JDBC) можно настроить для привязки разрешений к ролям.

4. Разрешения с подстановочными знаками

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

Мы представляем разрешения с подстановочными знаками в Shiro со строками. Строка разрешения состоит из одного или нескольких компонентов, разделенных двоеточием, таких как:

articles:edit:1

Значение каждой части строки зависит от приложения, так как Широ не применяет никаких правил. Однако в приведенном выше примере мы можем довольно четко интерпретировать строку как иерархию:

  1. Класс ресурсов, которые мы раскрываем (статьи)
  2. Действие на таком ресурсе (правка)
  3. Идентификатор конкретного ресурса, на котором мы хотим разрешить или запретить действие

Эта трехуровневая структура resource:action:id является распространенным шаблоном в приложениях Shiro, поскольку она проста и эффективна для представления множества различных сценариев.

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

Subject subject = SecurityUtils.getSubject();
if (subject.isPermitted("articles:edit:123")) {
    //Edit article with id 123
}

Обратите внимание, что количество компонентов в строке разрешений с подстановочными знаками не обязательно должно быть три, хотя обычно это три компонента.

4.1. Импликация разрешений и детализация на уровне экземпляра

Разрешения с подстановочными знаками сияют, когда мы объединяем их с другой особенностью разрешений Shiro — импликацией.

Когда мы проверяем роли, мы проверяем точное членство: либо у Субъекта есть определенная роль, либо ее нет. Другими словами, Широ проверяет роли на равенство.

С другой стороны, когда мы тестируем разрешения, мы тестируем на импликацию: является ли разрешение Субъекта просто тем, против которого мы его тестируем?

То, что подразумевается конкретно, зависит от реализации разрешения. Фактически, для разрешений с подстановочными знаками подразумевается частичное совпадение строк с возможностью использования подстановочных компонентов, как следует из названия.

Итак, предположим, что мы назначаем следующие разрешения роли author :

[roles]
author = articles:*

Затем всем, у кого есть роль автор , будут разрешены все возможные операции со статьями:

Subject subject = SecurityUtils.getSubject();
if (subject.isPermitted("articles:create")) {
    //Create a new article
}

То есть строка articles:* будет соответствовать любому подстановочному разрешению, первым компонентом которого является articles.

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

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

if (subject.isPermitted("articles:edit:1")) { //Better than "articles:*"
    //Edit article
}

5. Реализация Пользовательских Разрешений

Давайте кратко коснемся настроек разрешений. Несмотря на то, что разрешения с подстановочными знаками охватывают широкий спектр сценариев, мы можем заменить их решением, разработанным специально для нашего приложения.

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

Итак, что нам нужно?

  1. a Разрешение осуществление
  2. чтобы рассказать об этом Широ

Давайте посмотрим, как достичь обоих пунктов.

5.1. Написание реализации разрешения

A Разрешение реализация-это класс с одним методом — подразумевает :

public class PathPermission implements Permission {

    private final Path path;

    public PathPermission(Path path) {
        this.path = path;
    }

    @Override
    public boolean implies(Permission p) {
        if(p instanceof PathPermission) {
            return ((PathPermission) p).path.startsWith(path);
        }
        return false;
    }
}

Метод возвращает true , если это подразумевает другой объект разрешения, и возвращает false в противном случае.

5.2. Рассказать Широ О Нашей Реализации

Затем существуют различные способы интеграции реализации Permission в Shiro, но самый простой способ – внедрить пользовательский PermissionResolver в нашу Область :

IniRealm realm = new IniRealm();
Ini ini = Ini.fromResourcePath(Main.class.getResource("/com/.../shiro.ini").getPath());
realm.setIni(ini);
realm.setPermissionResolver(new PathPermissionResolver());
realm.init();

SecurityManager securityManager = new DefaultSecurityManager(realm);

PermissionResolver отвечает за преобразование строкового представления наших разрешений в фактические Разрешения объекты:

public class PathPermissionResolver implements PermissionResolver {
    @Override
    public Permission resolvePermission(String permissionString) {
        return new PathPermission(Paths.get(permissionString));
    }
}

Нам придется изменить наш предыдущий shiro.ini с разрешениями на основе пути:

[roles]
admin = /
editor = /articles
author = /articles/drafts

Затем мы сможем проверить наличие разрешений на пути:

if(currentUser.isPermitted("/articles/drafts/new-article")) {
    log.info("You can access articles");
}

Обратите внимание, что здесь мы программно настраиваем простую область. В типичном приложении мы будем использовать файл shiro.ini или другие средства, такие как Spring, для настройки Shiro и области. Реальный файл shiro.ini может содержать:

[main]
permissionResolver = com.baeldung.shiro.permissions.custom.PathPermissionResolver
dataSource = org.apache.shiro.jndi.JndiObjectFactory
dataSource.resourceName = java://app/jdbc/myDataSource

jdbcRealm = org.apache.shiro.realm.jdbc.JdbcRealm
jdbcRealm.dataSource = $dataSource 
jdbcRealm.permissionResolver = $permissionResolver

6. Заключение

В этой статье мы рассмотрели, как Apache Shiro реализует Управление доступом на основе разрешений.

Как всегда, реализации всех этих примеров и фрагментов кода доступны на GitHub .