Авторизация

Дата обновления перевода 2023-07-10

Авторизация

Когда любой из поставщикой аутентификации (см. Аутентификация) подтвердил все-еще-неавторизованный токен, будет возващён аутентифицированный токен. Слушатель аутентификации установит этот токен напрямую в TokenStorageInterface, используя его метод setToken().

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

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

  • Текущем токене
    Например, метод токена getRoleNames() может быть использован для извлечения ролей текущего пользователя (например, ROLE_SUPER_ADMIN), или решение может быть основано на классе токена.
  • Наборе атрибутов
    Каждый атрибут представляет определённое право, которое должен иметь пользователь, к примеру ROLE_ADMIN, чтобы гарантировать, что пользователь является администратором.
  • Объекте (опционально)
    Любой объект, для которого нужно проверять контроль доступа, вроде объекта статьи или комментария.

Доступ к менеджеру решений

Так как принятие решения о том, авторизирован ли пользователь производить определённое действие, может быть достаточно сложным процессом, сам стандартный AccessDecisionManager зависит от нескольких избирателей и выносит финальный вердикт, основываясь на всех голосах (положительных, отрицательных или нейтральных), которые он получил, и заданной стратегии.

Применение доступных опций в деталях:

1
2
3
4
5
6
7
8
9
use Symfony\Component\Security\Core\Authorization\AccessDecisionManager;

// экземпляры Symfony\Component\Security\Core\Authorization\Voter\VoterInterface
$voters = [...];

// экземпляр Symfony\Component\Security\Core\Authorization\Strategy\AccessDecisionStrategyInterface
$strategy = ...;

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

See also

Вы можете изменить стратегию по умолчанию в конфигурации .

Стратегии

5.4

Классы стратегий были представлены в Symfony 5.4. В более ранних версиях, стратегия передавалась в качестве строки.

Следуюшие стратегии входят в пакет компонента:

AffirmativeStrategy (по умолчанию)
предоставляет доступ как только существует хоть один избиратель, гарантирующий доступ;
ConsensusStrategy
проедоставить доступ, если есть больше избирателей, предоставляющих доступ, чем его запрещающих; если голоса распределились поровну, решение примнимается исходя из параметра конструктора $allowIfEqualGrantedDeniedDecisions, который по умолчанию true.
UnanimousStrategy
предоставлять доступ только, если ни один из избирателей его не запретил.
PriorityStrategy

предоставляет или запрещает доступ по первому избирателю, который не воздерживается;

5.1

Версия стратегии "priority" была представлена в Symfony 5.1.

Если все избиратели удержались от голосования, решение основывается на параметре конструкторв $allowIfAllAbstainDecisions, который поддерживается всеми встроенными стратегиями, и по умолчанию false.

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
 * Всегда выбирает третьего избирателя.
 */
class ThirdVoterStrategy implements AccessDecisionStrategyInterface
{
    public function decide(\Traversable $results): bool
    {
        $votes = 0;
        foreach ($results as $result) {
            if (++$votes === 3) {
                return $result === VoterInterface::ACCESS_GRANTED;
            }
        }

        return false;
    }
}

Избиратели

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

vote(TokenInterface $token, $object, array $attributes)
этот метод будет проводить голосование и возвращать значение, равное одной из констант класса VoterInterface, т.е. VoterInterface::ACCESS_GRANTED, VoterInterface::ACCESS_DENIED или VoterInterface::ACCESS_ABSTAIN;

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

AuthenticatedVoter

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

Он также поддерживает атрибуты IS_ANONYMOUS, IS_REMEMBERED, IS_IMPERSONATOR для предоставляения доступа, основываясь на конкретном состоянии аутентификации.

5.1

Атрибуты IS_ANONYMOUS, IS_REMEMBERED и IS_IMPERSONATOR были представлены в Symfony 5.1.

1
2
3
4
5
6
7
8
9
10
11
12
13
use Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolver;

$trustResolver = new AuthenticationTrustResolver();

$authenticatedVoter = new AuthenticatedVoter($trustResolver);

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

// any object
$object = ...;

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

RoleVoter

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

1
2
3
4
5
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_ADMIN, а также пользователям, которые имеют роль ROLE_SUPER_ADMIN:

1
2
3
4
5
6
7
8
9
10
use Symfony\Component\Security\Core\Authorization\Voter\RoleHierarchyVoter;
use Symfony\Component\Security\Core\Role\RoleHierarchy;

$hierarchy = [
    'ROLE_SUPER_ADMIN' => ['ROLE_ADMIN', 'ROLE_USER'],
];

$roleHierarchy = new RoleHierarchy($hierarchy);

$roleHierarchyVoter = new RoleHierarchyVoter($roleHierarchy);

ExpressionVoter

ExpressionVoter предоставляет доступ, основываясь на оценке выражений, созданных с помощью компонента ExpressionLanguage. Эти выражения имеют доступ к ряду специальных переменных безопасности:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
use Symfony\Component\ExpressionLanguage\Expression;
use Symfony\Component\Security\Core\Authorization\Voter\ExpressionVoter;

// Symfony\Component\Security\Core\Authorization\ExpressionLanguage;
$expressionLanguage = ...;

// экземпляр Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolverInterface
$trustResolver = ...;

// Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface
$authorizationChecker = ...;

$expressionVoter = new ExpressionVoter($expressionLanguage, $trustResolver, $authorizationChecker);

// экземпляр Symfony\Component\Security\Core\Authentication\Token\TokenInterface
$token = ...;

// любой объект
$object = ...;

$expression = new Expression(
    '"ROLE_ADMIN" in role_names or (not is_anonymous() and user.isSuperAdmin())'
);

$vote = $expressionVoter->vote($token, $object, [$expression]);

Note

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

Роли

Роли - это строки, которые предоставляют выражения определённому праву пользователя (например, "редактировать пост в блоге", "создать инвойс"). Вы можете свободно выбирать эти строки. Единственное требование - они должны начинаться с префикса ROLE_ (например, ROLE_POST_EDIT, ROLE_INVOICE_CREATE).

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

Слушатель доступа

Менеджер решений доступа может быть использован в любой момент запроса, чтобы решить, имеет ли текущий пользователь право доступа к заданному источнику. Одним необязательным, но полезным методом для ограничения доступа, основываясь на схеме URL, является AccessListener, который является одним из слушателей брандмауэра (см. Брандмауэр и авторизация), он вызывается для каждого запроса, совпадающего с картой брандмауэра (см. Брандмауэр и авторизация).

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
use Symfony\Component\HttpFoundation\RequestMatcher;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage;
use Symfony\Component\Security\Http\AccessMap;
use Symfony\Component\Security\Http\Firewall\AccessListener;

$accessMap = new AccessMap();
$tokenStorage = new TokenStorage();
$requestMatcher = new RequestMatcher('^/admin');
$accessMap->add($requestMatcher, ['ROLE_ADMIN']);

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

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

Менеджер решений доступа также доступен в других частях приложения через метод isGranted() класса AuthorizationChecker. Вызов этого метода напрямую делегирует вопрос менеджеру решений доступа:

1
2
3
4
5
6
7
8
9
10
11
12
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();
}