Обзор компонентов Symfony2 : Авторизация

В случае если любой из провайдеров аутентификации (см. Провайдеры аутентификации) подтвердит непроверенный токен, то будет возвращен аутентифицированный токен. Аутентификационный подписчик должен напрямую установить этот токен в TokenStorageInterface при помощи метода [setToken()](http://api.symfony.com/2.6/Symfony/Component/Security/Core/Authentication/Token/Storage/TokenStorageInterface.html#setToken()).

После этого пользователь считается аутентифицированным, то есть идентифицированным. Теперь в любой части приложения вы можете использовать этот токен для определения доступен ли определенный URL пользователю или может ли он изменять определенный объект. Само решение о праве доступа принимается экземпляром AccessDecisionManagerInterface.

Решение об авторизации всегда принимается основываясь на нескольких моментах:

  • Текущий токен - Например, метод [getRoles()](http://api.symfony.com/2.6/Symfony/Component/Security/Core/Authentication/Token/TokenInterface.html#getRoles()) токена можно использовать для определения ролей пользователя (ROLE_SUPER_ADMIN), а сам класс токена можно использовать для принятия решения о аутентификации.
  • Набор атрибутов - каждый атрибут отвечает за определенное право, которым обладает пользователь, например, ROLE_ADMIN говорит о том, что пользователь является администратором.
  • Объект (опционально) - любой объект, доступ к которому определяется, например, объект статьи или комментария.

TokenStorageInterface впервые был представлен в Symfony 2.6. В более ранних версиях приходилось использовать метод setToken() из SecurityContextInterface.

Менеджер доступа

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

Существует несколько стратегий:

  • affirmative (утвердительная, по-умолчанию) - доступ открывается сразу, как только один из voter’ов вернул положительный ответ
  • consensus (согласование) - досутп открывается, если voter’ов с положительным ответом больше, чем с отрицательным
  • (unanimous) единогласие - доступ открывается только в том случае, если все voter’ы дали утверждение
<?php

use Symfony\Component\Security\Core\Authorization\AccessDecisionManager;

// instances of Symfony\Component\Security\Core\Authorization\Voter\VoterInterface
$voters = array(...);

// one of "affirmative", "consensus", "unanimous"
$strategy = ...;

// whether or not to grant access when all voters abstain
$allowIfAllAbstainDecisions = ...;

// whether or not to grant access when there is no majority (applies only to the "consensus" strategy)
$allowIfEqualGrantedDeniedDecisions = ...;

$accessDecisionManager = new AccessDecisionManager(
   $voters,
   $strategy,
   $allowIfAllAbstainDecisions,
   $allowIfEqualGrantedDeniedDecisions
);

Стратегию принятия решения можно изменить в конфигурации.

Voter’ы

Все voter’ы является экземплярами VoterInterface, это означает, что каждый класс voter’а должен реализовать несколько методов для принятия решения:

  • supportsAttribute($attribute) - используется для определения способен ли voter обработать атрибут
  • supportsClass($class) - используется для определения может ли voter открыть или закрыть доступ к определенному классу
  • vote(TokenInterface $token, $object, array $attributes) - именно этот метод принимает решение об открытии или закрытии доступа и возвращает одну из следующих констант: VoterInterface::ACCESS_GRANTED, VoterInterface::ACCESS_DENIED или VoterInterface::ACCESS_ABSTAIN

Компонент Security содержит в себе несколько стандартных voter’ов, которых должно быть достаточно в большинстве случаев:

AuthenticatedVoter

AuthenticatedVoter поддерживает атрибуты IS_AUTHENTICATED_FULLY, IS_AUTHENTICATED_REMEMBERED, и IS_AUTHENTICATED_ANONYMOUSLY и открывает доступ в зависимости от текущего уровня аутентификации, то есть определяет аутентифицирован ли пользователь полностью или только при помощи cookie, или вообще аутентифицирован как аноним.

<?php

use Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolver;

$anonymousClass = 'Symfony\Component\Security\Core\Authentication\Token\AnonymousToken';
$rememberMeClass = 'Symfony\Component\Security\Core\Authentication\Token\RememberMeToken';

$trustResolver = new AuthenticationTrustResolver($anonymousClass, $rememberMeClass);

$authenticatedVoter = new AuthenticatedVoter($trustResolver);

// instance of Symfony\Component\Security\Core\Authentication\Token\TokenInterface
$token = ...;

// any object
$object = ...;

$vote = $authenticatedVoter->vote($token, $object, array('IS_AUTHENTICATED_FULLY');

RoleVoter

RoleVoter поддерживает атрибуты начинающиеся с ROLE_ и открывает доступ в том случае, если роль присутствует в массиве ролей пользователя, полученном при помощи метода getRoles():

<?php

use Symfony\Component\Security\Core\Authorization\Voter\RoleVoter;

$roleVoter = new RoleVoter('ROLE_');

$roleVoter->vote($token, $object, array('ROLE_ADMIN'));

RoleHierarchyVoter

RoleHierarchyVoter наследует RoleVoter и добавляет некоторый функционал к нему: он умеет обрабатывать иерархию ролей. Например, роль ROLE_SUPER_ADMIN может содержать подроли ROLE_ADMIN и ROLE_USER, таким образом если некоторый объект требует наличия у пользователя роли ROLE_ADMIN, то доступ будет открыт не только тому, кто имеет эту роль, но также и пользователю с ролью ROLE_SUPER_ADMIN:

<?php

use Symfony\Component\Security\Core\Authorization\Voter\RoleHierarchyVoter;
use Symfony\Component\Security\Core\Role\RoleHierarchy;

$hierarchy = array(
   'ROLE_SUPER_ADMIN' => array('ROLE_ADMIN', 'ROLE_USER'),
);

$roleHierarchy = new RoleHierarchy($hierarchy);

$roleHierarchyVoter = new RoleHierarchyVoter($roleHierarchy);

При создании своего voter’а вы, конечно, можете добавить в конструкторе необходимые вам зависимости.

Роли

Роли - объекты, отображающие определенный набор прав пользователя. Существует только одно правило - класс должен реализовать RoleInterface, то есть необходимо прописать методы [getRole()](http://api.symfony.com/2.6/Symfony/Component/Security/Core/Role/Role/RoleInterface.html#getRole()), который должен вернуть роль в строковом виде. Стандартный класс Role просто возвращает первый аргумент конструктора.

<?php

use Symfony\Component\Security\Core\Role\Role;

$role = new Role('ROLE_ADMIN');

// will echo 'ROLE_ADMIN'
echo $role->getRole();

Большинство токенов наследуют AbstractToken, таким образом роли переданные конструктору будут автоматически преобразованы из строк в объекты класса Role.

Использование менеджера доступа

Слушатели доступа (The Access Listener)

Менеджер доступа может быть использован в любой точке запроса для определения наличия прав у пользователя. Один необязательный, но полезный метод для ограничения доступа на основе шаблона URL - AccessListener, который по-сути является одим из нескольких подписчиков файрвола (см. Подписчики Файрвола). Он обрабатывает каждый запрос, сравнивая его с картой файрвола (см. Файрвол при работе с HTTP запросами).

Он использует карту доступа (должна реализовывать интерфейс AccessMapInterface), которая содержит в себе шаблоны запросов и соответствующий им набор атрибутов. Пользователь должен иметь иметь набор этих атрибутов для доступа к приложению.

<?php

use Symfony\Component\Security\Http\AccessMap;
use Symfony\Component\HttpFoundation\RequestMatcher;
use Symfony\Component\Security\Http\Firewall\AccessListener;

$accessMap = new AccessMap();
$requestMatcher = new RequestMatcher('^/admin');
$accessMap->add($requestMatcher, array('ROLE_ADMIN'));

$accessListener = new AccessListener(
   $securityContext,
   $accessDecisionManager,
   $accessMap,
   $authenticationManager
);

Проверка авторизации

Менеджер доступа доступен во всех частях приложения через метод [isGranted()](http://api.symfony.com/2.6/Symfony/Component/Security/Core/Authorization/AuthorizationChecker.html#isGranted()) класса AuthorizationChecker. Вызов этого метода напрямую передаёт ваш запрос менеджеру доступа:

<?php

use Symfony\Component\Security\Core\Authorization\AuthorizationChecker;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;

$authorizationChecker = new AuthorizationChecker(
   $tokenStorage,
   $authenticationManager,
   $accessDecisionManager
);

if (!$authorizationChecker->isGranted('ROLE_ADMIN')) {
   throw new AccessDeniedException();
}

Статьи из серии

Комментарии

0