Використання виразів у контролі доступу безпеки

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

Використання виразів у контролі доступу безпеки

See also

Найкращим рішенням для роботи зі складними правилами авторизації є використання Системи виборців.

На додаток до ролі типу ROLE_ADMIN, метод isGranted() також приймає обʼєкт Expression:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// src/Controller/MyController.php
namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\ExpressionLanguage\Expression;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Http\Attribute\IsGranted;

class MyController extends AbstractController
{
    #[IsGranted(new Expression('is_granted("ROLE_ADMIN") or is_granted("ROLE_MANAGER")'))]
    public function show(): Response
    {
        // ...
    }

    #[IsGranted(new Expression(
        '"ROLE_ADMIN" in role_names or (is_authenticated() and user.isSuperAdmin())'
    ))]
    public function edit(): Response
    {
        // ...
    }
}

У цьому прикладі, якщо поточний користувач має ROLE_ADMIN або якщо метод обʼєкта поточного користувача isSuperAdmin() повертає true, то доступ буде гарантовано (примітка: ваш обʼєкт Користувача може не мати методу isSuperAdmin(), цей метод було вигадано для цього прикладу).

Вираз безпеки повинен використовувати будь-який валідний синтаксис мови виразів і може використовувати будь-яку з цих змінних, створених Symfony:

user
Обʼєкт користувача (або рядок anon, якщо ви не аутентифіковані).
role_names
Масив ролей, які має користувач, з рядковим представленням. Цей масив включає в себе всі ролі надані не напряму через ієрархію ролей , але не включає атрибути IS_AUTHENTICATED_* (див. функції нижче).
object
Обʼєкт (якщо є), який передається в якості другого аргументу isGranted().
subject
Зберігає те ж значення, що і object, тому вони еквівалентні.
token
Обʼєкт токена.
trust_resolver
Обʼєкт AuthenticationTrustResolverInterface,: ви скоріш за все використовуватимете функції is_*(), описані нижче.

Крім того, у вас є доступ до наступних функцій всередині виразу:

is_authenticated()
Повертає true, якщо користувач аутентифікований через "запамʼятати мене" або "повністю" аутентифікований - тобто, повертає "true", якщо користувач знаходиться у системі.
is_remember_me()
Схоже, але не еквівалентно з IS_AUTHENTICATED_REMEMBERED, див. нижче.
is_fully_authenticated()
Схоже, але не евівалентно з IS_AUTHENTICATED_FULLY, див. нижче.
is_granted()
Перевіряє, чи є у користувача заданий дозвіл. Додатково приймає другий аргумент з обʼєктом, де перевіряється дозвіл. Еквівалентно використанню методу isGranted() з сервісу безпеки.

Функції is_remember_me() і is_authenticated_fully() схожі на використання IS_AUTHENTICATED_REMEMBERED і IS_AUTHENTICATED_FULLY з функцією isGranted(), але вони не однакові. Наступний уривок з контролера демонструє відмінності:

1
2
3
4
5
6
7
8
9
10
11
12
use Symfony\Component\ExpressionLanguage\Expression;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
// ...

public function index(AuthorizationCheckerInterface $authorizationChecker): Response
{
    $access1 = $authorizationChecker->isGranted('IS_AUTHENTICATED_REMEMBERED');

    $access2 = $authorizationChecker->isGranted(new Expression(
        'is_remember_me() or is_fully_authenticated()'
    ));
}

Тут, $access1 і $access2 матимуть однакове значення. На відміну від поведінки IS_AUTHENTICATED_REMEMBERED і IS_AUTHENTICATED_FULLY, функція is_remember_me() повертає "ture" лише якщо користувач аутентифікований через кукі "запамʼятати мене", а is_fully_authenticated повертає "true" лише якщо користувач увійшов в систему протягом цієї сесії (тобто, повнофункціональний).

У випадку атрибуту #[IsGranted()] суб'єктом також може бути об'єкт Expression:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// src/Controller/MyController.php
namespace App\Controller;

use App\Entity\Post;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\ExpressionLanguage\Expression;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Http\Attribute\IsGranted;

class MyController extends AbstractController
{
    #[IsGranted(
        attribute: new Expression('user === subject'),
        subject: new Expression('args["post"].getAuthor()'),
    )]
    public function index(Post $post): Response
    {
        // ...
    }
}

У цьому прикладі ми отримуємо автора посту і використовуємо його як субʼєкт. Якщо субʼєкт збігається з поточним користувачем, то доступ буде надано.

Субʼєкт також може бути масивом, де ключ можна використовувати як псевдонім для результату виразу:

1
2
3
4
5
6
7
8
9
10
11
#[IsGranted(
    attribute: new Expression('user === subject["author"] and subject["post"].isPublished()'),
    subject: [
        'author' => new Expression('args["post"].getAuthor()'),
        'post',
    ],
)]
public function index(Post $post): Response
{
    // ...
}

Тут доступ буде надано, якщо автор збігається з поточним користувачем і метод посту isPublished() повертає true.

Ви також можете використовувати поточний запит як субʼєк:

1
2
3
4
5
6
7
8
#[IsGranted(
    attribute: '...',
    subject: new Expression('request'),
)]
public function index(): Response
{
    // ...
}

Всередині виразу субʼєкта ви маєте доступ до двох змінних:

request
Об'єкт Symfony Request , який представляє поточний запит.
args
Масив аргументів контролера, які передаються контролеру.