Запретить пользователю вход в систему, если его статус неактивен - PullRequest
2 голосов
/ 21 февраля 2020

Я пытаюсь заблокировать вход пользователя в его статус неактивен. Я использую API-платформу с пакетом LexikJWT.

Я пытался создать JWTAuthentication охрану, расширяя JWTTokenAuthenticator->checkCredentials, но проблема в том, что это работает после того, как пользователь уже вошел в систему.

Чего я хочу добиться, так это вернуть пользователя a сообщение о том, что ему сначала необходимо активировать свою учетную запись, или любое другое сообщение, предпочтительно любое пользовательское сообщение в любом пользовательском состоянии.

Моя система безопасности YAML выглядит следующим образом:

security:
    encoders:
        App\Entity\User:
            algorithm: bcrypt
    providers:
        app_user_provider:
            entity:
                class: App\Entity\User
                property: email
    firewalls:
        dev:
            pattern: ^/_(profiler|wdt)
            security: false
        api:
            pattern: ^/api/
            stateless: true
            anonymous: true
            provider: app_user_provider
            json_login:
                check_path: /api/authentication_token
                username_path: email
                password_path: password
                success_handler: lexik_jwt_authentication.handler.authentication_success
                failure_handler: lexik_jwt_authentication.handler.authentication_failure
            guard:
                authenticators:
                    - app.jwt_token_authenticator
        main:
            anonymous: true
    access_control:
        - { path: ^/api/authentication_token,   roles: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/api/graphql,                roles: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/public-api,                 roles: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/api/,                       roles: [ROLE_MANAGER, ROLE_LEADER] }
        - { path: ^/,                           roles: IS_AUTHENTICATED_ANONYMOUSLY }

Услуги:

services:
    # default configuration for services in *this* file
    _defaults:
        autowire: true      # Automatically injects dependencies in your services.
        autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.

    # makes classes in src/ available to be used as services
    # this creates a service per class whose id is the fully-qualified class name
    App\:
        resource: '../src/*'
        exclude: '../src/{DependencyInjection,Entity,Migrations,Tests,Kernel.php}'

    # controllers are imported separately to make sure services can be injected
    # as action arguments even if you don't extend any base controller class
    App\Controller\:
        resource: '../src/Controller'
        tags: ['controller.service_arguments']

    # add more service definitions when explicit configuration is needed
    # please note that last definitions always *replace* previous ones
    gedmo.listener.softdeleteable:
        class: Gedmo\SoftDeleteable\SoftDeleteableListener
        tags:
            - { name: doctrine.event_subscriber, connection: default }
        calls:
            - [ setAnnotationReader, [ '@annotation_reader' ] ]

    acme_api.event.authentication_success_listener:
        class: App\EventListener\AuthenticationSuccessListener
        tags:
            - { name: kernel.event_listener, event: lexik_jwt_authentication.on_authentication_success, method: onAuthenticationSuccessResponse }

    app.jwt_token_authenticator:
        autowire: false
        autoconfigure: false
        class: App\Security\Guard\JWTTokenAuthenticator
        parent: lexik_jwt_authentication.security.guard.jwt_token_authenticator

    'App\Serializer\ApiNormalizer':
        decorates: 'api_platform.serializer.normalizer.item'
        arguments: ['@App\Serializer\ApiNormalizer.inner', '@doctrine.orm.entity_manager']

    'App\Serializer\HydraApiNormalizer':
        decorates: 'api_platform.jsonld.normalizer.item'
        arguments: ['@App\Serializer\ApiNormalizer.inner', '@doctrine.orm.entity_manager']

    'App\Voter\ModifyUserVoter':
        public: false
        tags:
            - { name: security.voter }

Защита аутентификатора

class JWTTokenAuthenticator extends BaseAuthenticator
{
    /**
     * {@inheritdoc}
     */
    public function checkCredentials($credentials, UserInterface $user)
    {
        if (!$user->getRoles() || !in_array($user->getRoles()[0], ['ROLE_MANAGER', 'ROLE_LEADER'])) {
            throw new UnauthorizedHttpException(rand(10000, 99999), 'Unauthorized');
        }

        if (!$user->getStatus() != "active") {
            throw new UnauthorizedHttpException(rand(10000, 99999), 'Unauthorized');
        }

        return true;
    }
}

Ответы [ 2 ]

2 голосов
/ 22 февраля 2020

Вам необходимо создать реализацию UserCheckerInterface. ( Документы )

Например, посмотрите на это:

use Symfony\Component\Security\Core\Exception\DisabledException;
use Symfony\Component\Security\Core\User\UserCheckerInterface;
use Symfony\Component\Security\Core\User\UserInterface;

class EasyUserChecker implements UserCheckerInterface
{
    public function checkPreAuth(UserInterface $user): void
    {
        // my checker only cares for our managed user classes, we return with no action 
        if (!$user instanceof AppAdmin && !$user instanceof AppUser) {
            return;
        }

        // our user entities can be deleted or disabled. If the user is neither, we return with no action
        if (!$user->isDeleted() && !empty($user->isEnabled())) {
            return;
        }

        // if we got here, we throw an exception
        throw new DisabledException('User account is disabled.');
    }

    // I'm not using the post authorization check, but needs to have an implementation to satisfy the interface.
    public function checkPostAuth(UserInterface $user): void
    {
    }
}

Вы включаете средство проверки в своей конфигурации безопасности. Например:

security:
    firewalls:
        api:
            pattern: ^/api
            user_checker: App\Security\EasyChecker

В настоящее время вы не должны писать новые реализации AdvancedUserInterface. Использование этого решения в качестве решения - неправильный способ go.

Этот интерфейс устарел начиная с 4.1 , и в целом удален in Symfony 5. Таким образом, код, основанный на этом, не может быть обновлен до более новых Symfony версий.

0 голосов
/ 21 февраля 2020

Мне удалось добиться того, что я хотел, реализовав AdvancedUserInterface вместо UserInterface в сущности User и добавив logi c к методу isEnabled().

...