Авторизація

Дата оновлення перекладу 2022-05-03

Авторизація

Коли будь-який з постачальників аутентифікації (див. Аутентификация) підтвердив все-ще-не-авторизований токен, буде повернено аутентифікований токен. Слухач аутентифікації встановить цей токен напряму в 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);

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

// будь-який об'єкт
$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();
}